Keyboard Ping Pong

(This question was inspired by this post.)


Given a string of letters, determine if the word "ping-pongs" across the keyboard. (Letters alternating between sides of the keyboard)

Use any established Latin-script keyboard layout that you would like (QWERTY, AZERTY, etc.) but please specify your choice in your answer.

Here is a graphic of the ping pong sides with QWERTY layout. The left hand side consists of the leftmost 5 letters, with the right hand side consisting of the remaining letters.

Test Cases (QWERTY)

skepticism      -      true
ENDOWMENT       -      true
quantity        -      true
dispel          -      true
kayaks          -      true
a               -      true
aa              -      false
test            -      false
WRONG           -      false
thiswillfail    -      false
ε               -      false


  1. Strings of length 1 should return true. Represents an ace!
  2. Strings of length 0 should return false. A point would not be scored in a real ping pong game.
  3. Should be case-insensitive

This question is tagged , so the shortest answer in bytes wins!

  • 10
    \$\begingroup\$ It seems questionable that the empty string is false. See vacuous truth. \$\endgroup\$
    – Arnauld
    Commented Jul 8 at 15:43
  • 8
    \$\begingroup\$ I think this question would be improved by excluding the empty string as a possible input, and fixing the keyboard layout to QWERTY. "Established" is vague and "etc" is even vaguer — as it stands, it's simply unclear which keyboard layouts make for valid answers. Questions are ideally self-contained, so I suggest you include the letters for half of every acceptable layout as a string instead of linking to a graphic. \$\endgroup\$
    – lynn
    Commented Jul 8 at 22:12
  • 7
    \$\begingroup\$ Maybe you can phrase the problem like: "Given a nonempty string of ASCII letters, decide if it alternates between letters among QWERTASDFGZXCVB and other letters, ignoring case." \$\endgroup\$
    – lynn
    Commented Jul 8 at 22:18
  • 2
    \$\begingroup\$ I'm voting to close this because this have multiple issues: established keyboard layout is not well-defined, and it did not mention what output formats are acceptable. I'm happy to take it back after it is fixed. \$\endgroup\$ Commented Jul 9 at 12:54
  • 2
    \$\begingroup\$ Do you want us to look through a list of keyboard layouts to find the most favorable one for the challenge? This sounds extremely tedious, barely related to code golfing, and ambiguous enough to inevitably lead to disagreement on what's an established layout. \$\endgroup\$
    – xnor
    Commented Jul 10 at 9:04

12 Answers 12


Perl 5 -p, 31 bytes


Try it online!

Assumes QWERTY layout.


Python, 69 62 60 58 55 54 bytes

-7 bytes thanks to @xnor. I independently arrived at a similar solution (only the first -4 though) since I didn't notice their comment for a while.
-1 byte thanks to @Albert.Lang

Takes input as a bytestring

lambda x,n=35782400:len({(n:=~n)>>j%32&1for j in x})%2

Attempt This Online!

  • 1
    \$\begingroup\$ -4 bytes using the walrus \$\endgroup\$
    – xnor
    Commented Jul 9 at 9:27
  • 1
    \$\begingroup\$ 55 bytes \$\endgroup\$
    – xnor
    Commented Jul 9 at 9:38
  • 1
    \$\begingroup\$ -1:lambda x,n=35782400:len({(n:=~n)>>j%32&1for j in x})%2 \$\endgroup\$ Commented Jul 10 at 5:28
  • \$\begingroup\$ For comparison, a variant approach also gets me 54 bytes. \$\endgroup\$
    – xnor
    Commented Jul 10 at 17:46
  • \$\begingroup\$ Would be 51 bytes if not for the empty string special case \$\endgroup\$
    – xnor
    Commented Jul 10 at 17:49

sed -r, 30 bytes


Try it online!

nearly a port of @Xcali's answer. I originally had my own but my regex was worse. errors with exit code of 1 if it's invalid, exits normally if it's ping pong

I just saw that we can use any keyboard we want, so here's my keyboard, colemak:

sed, 30 bytes


Try it online!

here's dvorak, the most popular alternate layout:

sed, 42 bytes


Try it online!

and here's workman, the third choice for weird people:

sed, 40 bytes


Try it online!

  • \$\begingroup\$ (if you were wondering, I did go through the rest of the keyboards on that wiki page, but none were as short as qwerty and colemak.) \$\endgroup\$
    – guest4308
    Commented Jul 8 at 17:18

Charcoal, 23 bytes


Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - if the word is ping-pong, nothing if not. Explanation:

 θ                      Input word
∧                       Logical And
  №                     Count of
          θ             Input word
         ⭆              Map over characters and join
           №            Count of
                      ι Current letter
                     ↧  Lowercased
              …h q      In range `h`...`q` (exclusive)
             ⪫    ω     Joined
            ⁺           Concatenated with
                   uy   Literal string `uy`
    01                  In literal string `01`
   …                    Cyclically extended to length
        θ               Input word
       L                Length
      ⊕                 Incremented
                        Implicitly print

JavaScript (ES6), 51 bytes

Expects an array of characters. Returns 0 for "ping-pong" or 1 for "empty or not ping-pong".


Try it online!

JavaScript (ES6), 39 bytes

@Xcali used a much more straightforward solution which can be ported as follows. Expects a string and returns a Boolean value (false for "ping-pong").


Try it online!


Python 3.8 (pre-release),  89   83  76 bytes

-6 bytes by walrusing 'yuiophjklnm'.
-7 bytes by Mukundan314.

lambda x:x and all((i in(r:='yuiophjklnm'))^(j in r)for i,j in zip(x,x[1:]))

Try it online!


Google Sheets, 76 bytes

=SORT(AND(1=LEN(IFERROR(SPLIT(REGEXREPLACE(A2,"(?i)[h-puy]"," $0 ")," "))))) 

enter image description here

Google Sheets, 47 bytes

Using @Xcali's idea


enter image description here


05AB1E, 20 19 bytes


Input as a list of characters.
Uses the QWERTY keyboard.

Try it online or verify all test cases.


žV       # Push ["qwertyuiop","asdfghjkl","zxcvbnm"]
   δ     # Map over each string:
  5 ô    #  Split it into parts of size 5
     ø   # Zip/transpose; swapping rows/columns
      J  # Join the inner lists together:
         #  ["qwertasdfgzxcvb","yuiophjklnm"]
I        # Push the input-list
 l       # Lowercase each inner character
  δ      # Double-vectorized over the two lists:
   å     #  Contains-check
    €    # Map over each inner list:
     ü   #  For each overlapping pair in this list:
      α  #   Take the absolute difference of the pair
       ß # Pop and push the flattened minimum
         # (1 if all are truthy; 0 if any are falsey; "" if empty)
Ig       # Push the length of the input-list
  Θ      # Check whether this length is exactly 1
M        # Push a copy of the largest value of the stack
         # (which is output implicitly as result)

Haskell, 70 66 bytes

  • -4 bytes thanks to xnor
f(x:y:z)=h x/=h y&&f(y:z)
f x=x>[]

Try it online!

  • First we map each letter to a boolean based on the hand (the helper function)
  • Then we assert the sequence is alternate
  • The base cases of 0 and 1 elements are handled by the last line
New contributor
DPD- is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • 1
    \$\begingroup\$ Nice answer! It looks like you can save a few bytes by applying the helper function directly to the elements, and writing x>[] in the base case: Try it online! \$\endgroup\$
    – xnor
    Commented Jul 11 at 4:22

Vyxal, 121 bitsv2, 15.125 bytes


Try it Online!



Outputs a space for true, an empty string for false. The footer in the link converts the result to 0 or 1 for convenience. Uses the qwerty layout.


[                  # ‎⁡Only execute the following if the input isn't the empty string.
# ‎⁢Why else do you think false is represented as the empty string? :p
# ‎⁣It was easier to special case it
 ⇩ƛ                # ‎⁤To each character in the lowercased input:
   k•              # ‎⁢⁡Push a list of each row of qwerty
     5vẇ           # ‎⁢⁢Split each into [first 5 characters, rest]
        ∑          # ‎⁢⁣And fold the list by addition. This gives a list of [left keys, right keys]
         vFT       # ‎⁢⁤Determine whether the character is a left or right key.
         vF        # ‎⁣⁡  Filter out the key from each side
             f¯    # ‎⁣⁣Flatten that and get the forward differences
# ‎⁣⁤This will be used to determine whether there's a pattern of left/right or right/left. 
# ‎⁤⁡A 0 in this list means that two characters in a row are from the same side. 
               A   # ‎⁤⁢Check whether all numbers are non-0, as per the above explanation of why. This will return either 0 or 1
                I  # ‎⁤⁣Push that many spaces. This is to be consistent with the empty string output. 

Created with the help of Luminespire.


Jelly, 13 bytes


A monadic Link that accepts a list of characters from A-Za-z and yields 1 if ping-pong or 0 otherwise.

Try it online! Or see the test-suite.


Nibbles, 23 nibbles (11.5 bytes)

`^,`'`=@=$`D2~ fe00f740

Attempt This Online!

Outputs 0 (falsy) if the input is a ping-pong string, or a nonzero value (truthy) if it isn't.

Can be adapted for various keyboard layouts by adjusting the 32-bit mask (here fe00f740 for left-hand keys on a QWERTY layout). This can't be shortened, even if all the left-hand keys are bunched together, as it needs to be 32 bits long to achieve case-insensitivity by modular indexing.

`^,`'`=@=$`D2~ fe00f740
     `=@                     # split input into chunks of same  
        =                    #   modular index of 
         $                   #     codepoint of each character
                             #   in 
          `D2  fe00f740      #     binary digits of fe00f740
                             #     (1 for left-hand letters, 0 otherwise);
   `'                        # now transpose this
  ,                          # and get the length
                             #   (0 if input was empty,
                             #    1 if input was ping-pong string,
                             #    >1 if any adjacent input characters were typed with the same hand)
`^           ~               # and bitwise-xor it with 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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