I saw there was only one other badminton related challenge right now. Since I play badminton myself (for the past 13 years now), I figured I'd add some badminton-related challenges. Here the first one:


Input: Two integers
Output: One of three distinct and unique outputs of your own choice. One indicating that the input is a valid badminton score AND the set has ended with a winner; one indicating that the input is a valid badminton score AND the set is still in play; one indicating the input is not a valid badminton score.

With badminton, both (pairs of) players start with 0 points, and you stop when one of the two (pairs of) players has reached a score of 21, with at least 2 points difference, up to a maximum of 30-29.

So these are all possible input-pairs (in either order) indicating it's a valid badminton score AND the set has ended:


And these are all possible input-pairs (in either order) indicating it's a valid badminton score BUT the set is still in play:


Any other pair of integer would be an invalid badminton score.

Challenge rules:

  • I/O is flexible, so:
    • You can take the input as a list of two numbers; two separated numbers through STDIN or function parameters; two strings; etc.
    • Output will be three distinct and unique values of your own choice. Can be integers (i.e. [0,1,2], [1,2,3], [-1,0,1], etc.); can be Booleans (i.e. [true,false,undefined/null/empty]); can be characters/strings (i.e. ["valid & ended","valid","invalid"]); etc.
    • Please specify the I/O you've used in your answer!
  • You are allowed to take the input-integers pre-ordered from lowest to highest or vice-versa.
  • The input integers can be negative, in which case they are of course invalid.

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer with default I/O rules, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code (i.e. TIO).
  • Also, adding an explanation for your answer is highly recommended.

Test cases:

These test cases are valid, and the set has ended:

0 21
12 21
21 23
28 30
29 30

These test cases are valid, but the set is still in play:

0 0
0 20
12 12
21 21
21 22

These test cases are invalid:

-21 19
-19 21
-1 1
12 22
29 31
30 30
42 43
1021 1021

13 Answers 13


Stax, 20 bytes


Run and debug it

It takes input in the same format as the examples. 0 means there's a valid winner. 1 means the game is in progress. -1 means invalid score.

In pseudo-code, with ordered inputs x and y, the algorithm is

sign(clamp(x + 2, 21, 30) - y) | (x < 0 || x >= 30 ? 0 : -1)
  • sign means numeric sign (-1, 0, or 1)
  • clamp forces its first argument into the specified half-open interval

Python 2, 97 95 75 72 71 70 69 64 55 54 52 51 50 48 bytes

lambda a,b:(b-61<~a<a>b/22*b-3)*~(19<b-(b<30)>a)

Try it online!

Takes input as pre-ordered a,b.

Returns -2, -1, 0 for ended, in play, invalid.

-1 byte, thanks to Kevin Cruijssen

Left part (b-61<~a<a>b/22*b-3) is a validity-check, and right part (19<b-(b<30)>a) is a check for game ended.


Python 2, 47 bytes

lambda a,b:[61>60-a>b<3+max(19,a)for b in-~b,b]

Try it online!

Outputs a list of two Booleans. Thanks to TFeld for writing a test suite in their answer that made it easy to check my solution.

ended: [False, True]
going: [True, True]
invalid: [False, False]

The key insight is that a valid score ends the game exactly if increasing the higher value b makes the score invalid. So, we just code up the validity condition, and check it for (a,b+1) in addition to (a,b) to see if the game has ended.

Validity is checked via three conditions that are chained together:

  • b<3+max(19,a): Checks that the higher score b isn't past winning, with either b<=21 or b<=a+2 (win by two)
  • 60-a>b: Equivalent to a+b<=59, ensuring the score isn't above (29,30)
  • 61>60-a: Equivalent to a>=0, ensures the lower score is non-negative

Python 2, 44 bytes

lambda a,b:[b-61<~a<a>b/22*b-3for b in-~b,b]

Try it online!

An improved validity check by TFeld saves 3 bytes. The main idea is to branch on "overtime" b>21 with b/22*b which effectively sets below-21 scores to zero, whereas I'd branched on a>19 with the longer max(19,a).

Python 2, 43 bytes

lambda a,b:a>>99|cmp(2+max(19,a)%30-a/29,b)

Try it online!


ended: 0
going: -1
invalid: 1

Assumes that the inputs are not below \$-2^{99}\$.

  • 1
    \$\begingroup\$ Using my newest validity-check (b-61<~a<a>b/22*b-3), you can save 3 bytes. \$\endgroup\$ – TFeld Mar 27 at 11:17
  • 1
    \$\begingroup\$ +1 byte to make your second solution work for all inputs: lambda a,b:-(a<0)|cmp(2+max(19,a)%30-a/29,b) \$\endgroup\$ – TFeld Mar 27 at 12:51

JavaScript (ES6),  55 53  48 bytes

Thanks to @KevinCruijssen for noticing that I was not fully assuming \$a\le b\$ (saving 5 bytes)

Takes input as (a)(b) with \$a\le b\$. Returns \$0\$ (valid), \$1\$ (ended) or \$2\$ (invalid).


Try it online!


C# (Visual C# Interactive Compiler), 53 52 bytes


Called as f(max)(min). Returns 3 for invalid, 1 for finished, 2 for ongoing.

Saved 1 byte thanks to Kevin Cruijjsen

Try it online!


Jelly, 25 bytes


Try it online!

Left argument: minimum. Right argument: maximum.
Invalid: 0. Ongoing: 1. Ended: 2.

Mathematically, this works as below (the left argument is \$x\$, the right is \$y\$):

$$[a]=\cases{a\colon1\\\lnot a\colon0}\\\otimes(a,b)=(a\bmod30,b\bmod31)\\x,y\in\mathbb Z\\X:=\min(\max(x+1,20),29)\\p:=(x,y)\\([X<y]+1)[X+2>y][p=\otimes p]$$


»19«28‘<‘×+2>ɗʋ⁹×,%Ƒ“œþ‘ɗ Left argument: x, Right argument: y
»19«28‘                   X := Bound x + 1 in [20, 29]:
»19                         X := max(x, 19).
   «28                      X := min(X, 28).
      ‘                     X := X + 1.
       <‘×+2>ɗʋ⁹          X := If X + 2 <= y, then 0, else if X < y, then 2, else 1:
       <                    t := If X < y, then 1, else 0.
        ‘                   t := t + 1.
          +2>ɗ              u := Check if X + 2 > y:
          +2                  u := X + 2.
            >                 u := If u > y, then 1, else 0.
         ×                  X := t * u.
                 ,%Ƒ“œþ‘ɗ z := If x mod 30 = x and y mod 31 = y, then 1, else 0:
                 ,          z := (x, y).
                  % “œþ‘    m := z mod (30, 31) = (x mod 30, y mod 31).
                   Ƒ        z := If z = m, then 1, else 0.
                ×         X * z.

VDM-SL, 80 bytes

f(i,j)==if(j-i>2and j>21)or(i<0or i=30or j>30)then{}else{(j>20and j-i>1or j=30)} 

This function takes the scores ordered in ascending order and returns the empty set if the score is invalid or the set containing whether the set is complete (so {true} if the set is complete and valid and {false} if the set is incomplete and valid)

A full program to run might look like this:

f:int*int+>set of bool
f(i,j)==if(j-i>2and j>21)or(i<0or i=30or j>30)then{}else{(j>20and j-i>1or j=30)}


if(j-i>2 and j>21)             /*if scores are too far apart*/
or(i<0 or i=30 or j>30)        /*or scores not in a valid range*/
then {}                        /*return the empty set*/
else{                       }  /*else return the set containing...*/
     (j>20 and j-i>1 or j=30)  /*if the set is complete*/

Java (JDK), 59 48 bytes


Try it online!

Returns an Object, which is the Integer 0 for invalid games and the Booleans true and false for valid ongoing games and for valid finished games respectively. Takes the score ordered (and curried), with the higher score first.

-2 bytes by inverting the end-of-match check.
-11 bytes by currying, using bitwise operators, and some return type autoboxing trickery - thanks to @KevinCruijssen


a->                      // Curried: Target type IntFunction<IntFunction<Object>>
    b->                  // Target type IntFunction<Object>
                         // Invalid if:
            b<0          //    Any score is negative
          | b > 29       //    Both scores above 29
          |   a > b + 2  //    Lead too big
            & a > 21     //        and leader has at least 21 points
          | a > 30       //    Anyone has 31 points
        ? 0              // If invalid, return 0 (autoboxed to Integer)
                         // If valid, return whether the game is ongoing (autoboxed to Boolean)
                         // Ongoing if:
        :   a < 21       //    Nobody has 21 points
          |   a < 30     //    Leader has fewer than 30 points
            & a < b + 2  //        and lead is small

APL (Dyalog Unicode), 35 bytesSBCS

Infix tacit function where ended is 2, ongoing is 1, invalid is 0, smaller and larger scores are left.

(,≡30 31|,)×(⊢<2+X)×1+⊢>X←29⌊20⌈1+⊣

Try it online!

Implements Erik the Outgolfer's mathematical formulas combined into

$$X:=\min(\max(x+1,20),29)\\\ ([X< y]+1)[X+2>y][(x,y)=(x\bmod30,y\bmod31)]$$ rearranged (as if traditional mathematical notation had vectorisation and inline assignments) to

$$[(x,y)=(x,y)\bmod(30,31)]×[y<2+X]×(1+[y< (X:=\min(29,\max(20,1+x)))])$$

and translated directly to APL (which is strictly right-associative, so we avoid some parentheses):

$$((x,y)≡30\ 31​|​x,y)×(y<2+X)×1+y>X←29​⌊​20​⌈​1 +x$$

This can be converted into a tacit function simply by substituting \$⊣\$ for \$x\$ and \$⊢\$ for \$y\$, symbolising the left and right arguments rather than the two variables:

$$((⊣​,⊢)≡30\ 31​|⊣​,⊢)×(⊣​<2+X)×1​+⊢​>X←29​⌊​20​⌈​1​+⊣$$

Now \$⊣⎕⊢\$ is equivalent to \$⎕\$ for any infix function \$⎕\$, so we can simplify to

$$(,​≡30\ 31​|​,)×(⊣​<2+X)×1​+⊢​>X←29​⌊​20​⌈​1​+⊣$$

which is our solution; (,≡30 31|,)×(⊢<2+X)×1+⊢>X←29⌊20⌈1+⊣:

 the left argument; \$x\$
1+ one plus that; \$1+x\$
20⌈ maximum of 20 and that; \$\max(20,…)\$
29⌊ minimum of 29 and that; \$\min(29,…)\$
X← assign that to X; \$X:=…\$
⊢> is the right argument greater (0/1)?; \$[y>…]\$
1+ add one; \$1+…\$
( multiply the following by that; \$(…)×…\$
2+X two plus X; \$2+X\$
⊢< is the right argument less than that (0/1); \$[y<…]\$
( multiply the following by that; \$(…)×…\$
, concatenate the arguments; \$(x,y)\$
30 31| remainders when divided by these numbers; \$…\mod(30,31)\$
,≡ are the concatenated arguments identical to that (0/1)?; \$[(x,y)=…]\$


x86 Assembly, 42 Bytes

Takes input in ECX and EDX registers. Note that ECX must be greater than EDX.
Outputs into EAX, where 0 means the game's still on, 1 representing the game being over and -1 (aka FFFFFFFF) representing an invalid score.

31 C0 83 F9 1E 77 1F 83 FA 1D 77 1A 83 F9 15 7C 
18 83 F9 1E 74 12 89 CB 29 D3 83 FB 02 74 09 7C 
08 83 F9 15 74 02 48 C3 40 C3

Or, more readable in Intel Syntax:

    CMP ECX, 30     ; check i_1 against 30
    JA .invalid     ; if >, invalid.
    CMP EDX, 29     ; check i_2 against 29
    JA .invalid     ; if >, invalid.
    CMP ECX, 21     ; check i_1 against 21
    JL .runi        ; if <, running.
    CMP ECX, 30     ; check i_1 against 30
    JE .over        ; if ==, over.
    SUB EBX, EDX    ; EBX = i_1 - i_2
    CMP EBX, 2      ; check EBX against 2
    JE .over        ; if ==, over.
    JL .runi        ; if <, running.
                    ; if >, keep executing!
    CMP ECX, 21     ; check i_1 against 21
    JE .over        ; if ==, over.
                    ; otherwise, it's invalid.
    ; fallthrough!
        DEC EAX     ; EAX = -1
        INC EAX     ; EAX = 1
    ; fallthrough!
        RETN        ; EAX = 0 or 1

Fun fact: this function almost follows the C Calling Convention's rules on which registers to preserve, except I had to clobber EBX to save some bytes on stack usage.

Optional (not included in byte-count)

By adding the following 6 bytes directly before start of the code above, you can pass ECX and EDX unordered:

39 D1 7D 02 87 CA

Which is the following in readable Intel Syntax:

JGE check

Retina 0.8.2, 92 bytes


Try it online! Link includes test cases. Takes input in ascending order. Explanation: The first stage simply converts from decimal to unary so that the scores can be properly compared. The second stage contains six alternate patterns, grouped into three groups so that three distinct values can be output, which are 10 for win, 01 for ongoing and 00 for illegal. The patterns are:

  • Against 0-19, a score of 21 is a win
  • Against 20-28, a score of +2 is a win
  • Against 29, a score of 30 is a win
  • Against any (lower) score, a score of 0-20 is ongoing
  • Against a score of up to 28, a score of +1 is ongoing
  • Anything else (including negative scores) is illegal

APL (Dyalog Unicode), 33 32 bytesSBCS

{h⍵+1 0}+h←(⊢≡31 30|⊢)×21 2∨.≥-\

Try it online!

in: a pair in descending order

out: 2=ongoing, 1=ended, 0=invalid

tests stolen from Adám's answer


Bash 4+, 97 89 91 88 bytes

Assume that inputs are ascending. Used concepts from VDM-SL answer. Try it Online
z==0 - game in progress
z==1 - game completed
z==2 - invalid

-8 by bracket cleanup from (( & | )) conditions
+2 fixing a bug, thanks to Kevin Cruijssen
-3 logic improvements by Kevin Cruijssen

i=$1 j=$2 z=0
echo $z
  • 1
    \$\begingroup\$ Your 89 bytes version seems to output 1 instead of 2 for 0 30. Your 97 bytes version was working correctly, so if you're unable to fix it, you can always rollback. Upvoted for that 97 version. :) \$\endgroup\$ – Kevin Cruijssen Mar 28 at 13:59
  • 1
    \$\begingroup\$ This should fix it, and golf a byte at the same time. :) \$\endgroup\$ – Kevin Cruijssen Mar 28 at 14:05
  • 1
    \$\begingroup\$ I fixed it, but yours was better! Hard to keep up :P \$\endgroup\$ – roblogic Mar 28 at 14:14
  • \$\begingroup\$ Bug at 29 30 :( it should be "completed" \$\endgroup\$ – roblogic Apr 3 at 8:39
  • 1
    \$\begingroup\$ Ah oops.. the i>29 should be j>29 in the second ternary to fix it. \$\endgroup\$ – Kevin Cruijssen Apr 3 at 8:42

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.