# Are These Scores Possible?

## Background

In the sport of Table Tennis (aka Ping-Pong or Whiff Whaff), two opponents play a sequence of rounds, where each round consists of players hitting a ball back and forth until one player (may or may not be the server) gains a point.

Table Tennis has some official rules that make for a good game, but we will use a different set of rules for a better challenge.

The modified rules are as follows:

1. The score is announced directly before each serve as a pair (current server's score, other player's score).
2. Person A serves for 5 points, then Person B serves for 5 points, then back to A. Hence, A serves whenever the total score A+B is 0-4 mod 10.
3. After each serve, either A scores a point or B scores a point. A and B both start with 0 points.
4. For simplicity, games never end.

Following is an example game:

(A starts serving, so the scores are read as (A,B))
0,0; A scores a point
1,0; B scores a point
1,1; A scores a point
2,1; A scores a point
3,1; A scores a point
(B is now serving, so the scores are read as (B,A))
1,4; A scores a point
1,5; B scores a point
2,5; B scores a point
3,5; B scores a point
4,5; B scores a point
(A is now serving, so the scores are read as (A,B))
5,5; B scores a point
5,6 …
(game continues)


Given a pair of unique score readouts, determine if they can be announced in the same game.

Your program/function may take input as any reasonable way equivalent to an ordered pair of numbers.

The output can follow your language's convention for truthy/falsey or use any two distinct values to represent true/false.

## Examples

Given (4,5), (1,4), the output should be truthy. The example game is one where this score set occurs.

Given (4,2), (3,5), the output should be falsey. They occur at point totals 6 and 8 respectively, so B is serving in both readouts, so both are reported as (B,A). It is impossible for B's score to decrease from 4 to 3 while A's score increases from 2 to 5, so this situation is impossible.

Given (3,1), (1,5), the output should be truthy. (3,1) is reported as (A,B), while (1,5) is reported as (B,A), so the game can transition from (3,1) to (1,5) if A scores 2 points.

## Test Cases

Truthy:
(4,5), (1,4)
(3,1), (1,5)
(0,0), (0,1)
(0,0), (45,54)
(6,9), (11,9)

Falsey:
(12,5), (11,6)
(4,2), (3,5)
(3,3), (5,2)
(2,1), (4,1)
(17,29), (17,24)

• "Hence, A serves whenever the sum of the scores of A and B in 0,1,2,3,4 mod 5." Huh? – xnor Jul 16 '20 at 10:24
• @xnor Probably should be 0-4 (mod 10). – Neil Jul 16 '20 at 12:26
• Oops, edited now. – fireflame241 Jul 16 '20 at 18:35
• Would be interesting to state that the second pair is supposed to happen after the first. I mean there is a temporary relationship with the position. – Noman_1 Jul 17 '20 at 9:41

# Python 2, 58 bytes

lambda a,b,A,B:(A-a)*(B-b)<(a-b)*(A-B)*((a+b)/5+(A+B)/5&1)


Try it online!

Outputs True/False reversed

59 bytes

lambda a,b,A,B:A*B+a*(b-A-B)<(b-a)*[A,B][(a+b)/5+(A+B)/5&1]


Try it online!

59 bytes

lambda a,b,A,B:A*B+b*a<[A*b+B*a,a*A+b*B][(a+b)/5+(A+B)/5&1]


Try it online!

62 bytes

a,b,A,B=input()
if(a+b)/5+(A+B)/5&1:a,b=b,a
print(A-a)*(B-b)<0


Try it online!

# Perl 5-p-Minteger, 77, 73 bytes

/ (.*) (.*) /;$_=($-$2)*($1-$')>=(($+$1)/5+($2+$')/5)%2*($-$1)*($2-$')  Try it online! 2 bytes saved using xnor approach, and 2 other bytes using integer division, explanation is (renaming:$ -> a, $1 -> b,$2 -> c, \$' -> d), first answer was:

• (a-B)(b-A)>=0 if scores are in reversed order ((a+b)/5%2^(A+B)/5%2==1)
• (a-A)(b-B)>=0 otherwise

as (a-B)(b-A)>=0 is equivalent to (a-A)(b-B)>=(a-b)(A-B)

• (a-B)*(b-A)>=0 <=> ab+AB-aA-bB>=0 <=> ab+AB-aB-Ab>=aA+bB-aB-bA <=> (a-A)(b-B)>=(a-b)(A-B)

• (a-A)(b-B)>=0 if (a+b)/5%2^(A+B)/5%2==1
• (a-A)(b-B)>=(a-b)(A-B) if (a+b)/5%2^(A+B)/5%2==0

or

• (a-A)(b-B)>=(a-b)(A-B)*((a+b)/5%2^(A+B)/5%2)

or with integer division

• (a-A)(b-B)>=(a-b)(A-B)*((a+b)/5+(A+B)/5)%2

# 05AB1E, 1514 12 bytes

εDO5÷FR]-Pd


-2 bytes thanks to @Neil.

Explanation:

ε        # Map both pairs in the (implicit) input to:
D       #  Duplicate the pair
O      #  Pop this duplicate and calculate its sum
5÷    #  Integer-divide it by 5
F   #  Loop that many times:
R  #   Reverse the pair every iteration
#   (the pair is reversed for odd sums; and remains unchanged for even sums)
]        # Close both the loop and map
# (all pairs are now in the order [A,B])
# Pop and push both pairs separated to the stack
-      # Subtract the values of the pairs from one another at the same indices
P     # Take the product of those two values
d    # And check that it's non-negative / >=0 (thus no score is decreasing)
# (after which the result is output implicitly)

• Doesn't εDO5÷._}{-Pd work? – Neil Jul 16 '20 at 9:59
• @Neil Ah, I guess you're indeed right, since the scores subtracted from one another can never be both negative or zero. It's either none or one of the two. Thanks – Kevin Cruijssen Jul 16 '20 at 10:12
• ... actually I meant εDO5÷._}-Pd... – Neil Jul 16 '20 at 10:24
• (the point being that if exactly one of the differences is negative then the score is impossible, but if two are equal or neither or both are negative then the score is possible) – Neil Jul 16 '20 at 10:25
• (I guess ._} and FR] are interchangeable in these versions too.) – Neil Jul 16 '20 at 12:26

# APL (Dyalog Extended), 15 bytes

≡⍥⍋∘⌽⍨⌊⍤+.÷∘5⌽⊢


Try it online!

≤⌿⍣2⍤∧⌊⍤+.÷∘5⌽⊢


Try it online!

It took a long time to shave one byte from 16. You can see a long history of alternative solutions in the TIO link.

Both solutions take a 2×2 matrix as single input, where each row is a score readout. The first one would also work in Dyalog APL 18.0, since it only uses 17.x features plus ⍥⍤.

### How it works

≡⍥⍋∘⌽⍨⌊⍤+.÷∘5⌽⊢  ⍝ Input: 2×2 matrix
⌊⍤+.÷∘5    ⍝ For each row, divide each number by 5, sum, then floor
⌽⊢  ⍝ Rotate each row by that amount
∘⌽⍨           ⍝ Check for the above and its horizontal reverse...
≡⍥⍋              ⍝   Is the sorting order equal?

≤⌿⍣2⍤∧⌊⍤+.÷∘5⌽⊢  ⍝ Alternative solution
⌊⍤+.÷∘5⌽⊢  ⍝ Same as above up to here
⍤∧           ⍝ Ascending sort the rows
≤⌿⍣2             ⍝ 1st axis reduce by ≤ twice;
⍝ Since the rows are sorted, first ≤⌿ gives [1 x]
⍝ where x is 1 iff the 2nd column is also increasing
⍝ Then the second ≤⌿ tests if x is 1


# APL (Dyalog) 18.0, 19 18 17 bytes

0≤×.-⍥(⌊⍤+.÷∘5⌽⊢)


Try it online!

This makes use of the new operators over (⍥) and atop (⍤), both of which are very good for golfing. TIO is still on 17.0, so this doesn't work, but I've tested it locally for the given test cases. Takes the two inputs as the left and right parameters.

• Fails at (4,4),(4,3), which should give true. – Bubbler Jul 16 '20 at 9:02
• @Bubbler reverted to a previous version (which is a bit longer) – Jo King Jul 16 '20 at 9:17

# Charcoal, 30 bytes

ＵＭθＥι§ι⁺÷Σι⁵μ¬›⁰ΠＥ²⁻§§θ⁰ι§§θ¹ι


Try it online! Link is to verbose version of code. Takes a pair of pairs of integers and outputs a Charcoal boolean, i.e. - for possible, nothing for impossible. Explanation:

ＵＭθＥι§ι⁺÷Σι⁵μ


Cyclically rotate each pair by one fifth of its sum.

¬›⁰ΠＥ²⁻§§θ⁰ι§§θ¹ι


Check that the product of the differences between the pairs is not negative.

# Jelly, 11 bytes

ṚS:5Ɗ¡€Ṣ>/E


A monadic Link accepting a list of the to pairs which yields a 1 if valid, or a 0 if not.

Try it online! Or see the test-suite.

### How?

ṚS:5Ɗ¡€Ṣ>/E - Link: list, S            e.g. [[17,29],[17,24]]
€     - for each (pair in S):
¡      -   repeat...
Ṛ           -   ...what: reverse
S          -       sum                       46     41
5        -       five                       5      5
:         -       integer division           9      8  -> [[29,17],[17,24]]
Ṣ    - sorted                                        [[17,24],[29,17]]
/  - reduce by:
>   -   is greater than?       ([17>?29, 24>?17])   [0,1]
E - all equal?                                    0


# Jelly, 2623 20 bytes

Thanks to fireflame241, I was able to shave off a few bytes already.

S€%⁵:5E¬µ³U⁸¡Ðo_/ṠIỊ


Original:

S€%ȷ1<5E¬©µ³Ṛ€®¡Ðo_/ṠIA=2¬


Try it online!

Here's my Jelly solution. It's my first Jelly program, so it can definitely be improved a lot, especially at the part where I use the register. I don't quite understand how argument flow works yet.

Here's how it works:

S€%⁵:5E¬µ³U⁸¡Ðo_/ṠIỊ - Example input:                     [[4,5],[1,4]]

S€%⁵<5               - Figure out who is serving each time
S€                   - Calculates sum of each inner list: [9, 5]
%⁵                 - Modulo 10:                         [9, 5]
<5               - Vectorized less than 5?:           [0, 0]

E¬µ³U⁸¡Ð       - If server is different, reverse list 1
E              - Checks if all elements are equal:  1
¬             - Logical not:                       0
µ            - Start a monadic chain
³           - Get first input:                   [[4,5],[1,4]]
U          - Reverse order of inner lists
Ðo      - At odd indices (ie. the first list)
⁸¡        - {left argument, ie. 0} times:      [[4,5],[1,4]]

_/ṠIỊ - Calculate difference in score per player.
If one decreased and the other increased,
it's not a possible score
_/    - Vectorized subtract:               [3, 1]
Ṡ   - Get sign:                          [1, 1]
I  - Difference between elements:       0
Ị - abs(x) <= 1:                       0
$$$$

• Good first answer! Some immediate 1-byte improvements: ⁵ also gives 10, Ṛ€ is the same as U in this application, and _/ is the same as I since both apply only to the first axis and vectorize the subtraction. Have a look at Jonathan Allan's solution to consider almost similar approach implemented with more experience. In general, registers usually mean there's a better way – fireflame241 Jul 17 '20 at 2:22
• Thanks for the tips! I just tried replacing the _/ with I, and it does seem to do the same thing (although the signs are flipped), but then the next I stops returning anything. Any idea why that's happening? – pslessard Jul 17 '20 at 2:46
• Oh, I returns a list, then it vectorizes the second time you use it. It's not just the negative of _/. – fireflame241 Jul 17 '20 at 3:13

# R, 6559 57 bytes

(or 56 bytes by outputting FALSE for Truthy and TRUE for Falsey)

function(a,b)prod(a-b[(0:1+sum(a)/5-sum(b)%/%5)%%2+1])>=0


Try it online!

Edit: -6 bytes by flipping b elements using indexing, instead of using if-else

Edit 2: -2 bytes by not bothering to do integer division for both a and b, since indexing will only use the integer part anyway.

Commented version:

validscores=function(a,b){
b=b[                    # Select elements from b
(0:1+                   # with index of: zero or 1, plus
sum(a)%/%5              # changes of serve until first score
-sum(b)%/%5)            # minus changes of serve until second score
%%2+1]                  # modulo 2, plus 1.
# This will flip the elements of b if there
# have been an odd number of changes of serve,
# & otherwise leave b as it was).
prod(a-b)>=0            # a-b now gives changes in each players points:
}                           # so if the second score came after the first,
# both changes must be >=0, otherwise both must be <=0.
# So, either way, the product must be >=0


# Java 8, 11663 54 bytes

(a,b,A,B)->(A-a)*(B-b)<((a+b)/5+(A+B)/5)%2*(a-b)*(A-B)


Whopping -53 bytes thanks to @NahuelFouilleul as a port from his Perl answer, so make sure to upvote him!!

Takes the inputs as four loose integers.
Outputs false for truthy; and true for falsey.

Try it online.

Explanation:

(a,b,A,B)->   // Method with four integer parameters and boolean return-type
(A-a)*(B-b) //   Get the difference between the values of the two pairs,
//   and take the product of those two differences
<           //  And check that this is smaller than:
((a+b)     //     The sum of the first pair
/5       //     integer-divided by 5
+         //    Plus:
(A+B)/5) //     The sum of the second pair, integer-divided by 5 as well
%2         //   Check if these two added together are odd (1 if odd; 0 if even)
*(a-b)     //   Multiplied by the reduced by subtraction of the first pair
*(A-B)     //   Multiplied by the reduced by subtraction of the second pair

• 63 bytes, from my perl submission – Nahuel Fouilleul Jul 16 '20 at 12:52
• @NahuelFouilleul Thanks, nice approach. Never even used ^ as higher order precedence with booleans. I use | or & plenty, but ^ (with booleans) next to never. :) – Kevin Cruijssen Jul 16 '20 at 13:15
• even shorter using xnor approach – Nahuel Fouilleul Jul 16 '20 at 13:23

# C (gcc), 58 bytes

f(a,b,A,B){a=(A-a)*(B-b)<(a-b)*(A-B)*((a+b)/5+(A+B)/5&1);}


Try it online!

Outputs $$\0\$$ if they can be announced in the same game and $$\1\$$ otherwise.
(a,b,c,d)=>(~~((a+b)%10/5)==~~((c+d)%10/5)?(c-a)*(d-b):(d-a)*(c-b))>=0
`