31
\$\begingroup\$

Inspired by this video by Matt Parker

The distances between the letter keys of a QWERTY keyboard are somewhat standardised. The keys are square and both the horizontal and vertical spacing are 19.05mm (so if there were no gaps between the keys, their side lengths would be 19.05mm), and the three rows of keys are offset by ¼ and ½ a key size. Here is a diagram:

Diagram of a QWERTY keyboard with the non-letter keys blurred and the distances between the letter keys annotated

Your task is simple: given two letters, output the Euclidean distance between their centres (or between any two equivalent relative positions, for that matter) on a QWERTY keyboard as described above.

The rows of the QWERTY keyboard in a more easily copyable format are:

QWERTYUIOP
ASDFGHJKL
ZXCVBNM

Q, A, and Z are aligned to the left with only the ¼ and ½ offsets described above; the keys at the rightmost end (P, L, and M) do not line up as well.

Of course, given the same letter twice, your output should be 0.

Rules

  • You may accept input in uppercase or lowercase, and as characters, strings, or ASCII codepoints, but this must be consistent
    • You may assume the input will always be valid; behaviour for non-Latin-letters is undefined
  • Your output must be in millimeters, and be accurate to within 0.1mm of the actual value
  • You may use any reasonable I/O method
  • Standard loopholes are forbidden
  • This is , so the shortest code in bytes wins

Test cases

A full list can be found here (original list calculated by Matt Parker, published here)

In  Out

A B 87.82
B Q 98.18
G C 34.34
H J 19.05
L J 38.10
P X 143.27
Y Y 0
4 K [behaviour undefined]

Note: of course your keyboard has different measurements. Of course your keyboard uses AZERTY, or Dvorak, or something else. Of course your keyboard is 3-dimensional with keys that aren't completely flat, so the distances vary a little. Of course your keyboard has wobbly keys that mean the distances aren't even constants. Of course you live in a universe with Heisenberg's Uncertainty Principle in which you cannot truly know that the keys are that far apart. This is obviously an idealised model of a keyboard; please don't argue about these things in the comments!

Image above modified from work by Denelson83 on English Wikipedia, used under CC-BY-SA 3.0.

\$\endgroup\$
7
  • \$\begingroup\$ Sandbox \$\endgroup\$
    – pxeger
    Commented Aug 22, 2021 at 10:55
  • 8
    \$\begingroup\$ Well my keyboard only has the letter P, so the example keyboard is clearly wrong :P \$\endgroup\$ Commented Aug 22, 2021 at 11:08
  • 4
    \$\begingroup\$ My keyboard doesn't even require Pythagoras! \$\endgroup\$ Commented Aug 22, 2021 at 13:18
  • \$\begingroup\$ Regarding the \$0.1\$mm accuracy - does this mean we may produce values that while being within \$0.1\$mm of the true result would be incorrect when rounded to one decimal place? (e.g. AJ is precisely \$19.05 \times 6 = 114.3\$ so may we output \$114.24\$ since it is within \$0.1\$, or not since it would not round to \$114.3\$?) \$\endgroup\$ Commented Aug 23, 2021 at 11:14
  • \$\begingroup\$ Thanks, I've updated the specification accordingly. \$\endgroup\$ Commented Aug 23, 2021 at 16:25

13 Answers 13

10
\$\begingroup\$

Jelly,  25 24 23  21 bytes

-1 thanks to Nick Kennedy! (use of ) to avoid repeated mapping)

-2 thanks to emanresu A! (Accuracy is only required to be within \$0.1\$mm of the true value)

ØQœi×4:3+ɗ\)ạ/×4.76ÆḊ

A monadic Link that accepts a list of two upper-case characters and yields a floating-point number.

Try it online! Or see the test-suite.

How?

Finds horizontal and vertical distances measured in quarter-key lengths of \$\frac{19.05}{4} = 4.7625\$ then takes the norm of the vector (i.e. uses Pythagoras). Since \$4.76\$ is good enough* for requirements, that is used as the quarter-key length.

ØQœi×4:3+ɗ\)ạ/×4.76ÆḊ - Link: characters, [A, B]
           )          - for each (c in [A, B]):
ØQ                    -   Qwerty keyboard -> ["Q..P","A..L","Z..M"]
  œi                  -   first multi-dimensional index of c in Qwerty keyboard
    ×4                -   multiply by four
                          -> [row(c)×4, column(c)×4]
          \           -   cumulative reduce by:
         ɗ            -     last three links as a dyad:
       3              -       three
      :               -       (row(c)×4) integer divide (3)  i.e. 4->1; 8->2; 12->4
        +             -       add (column(c)×4)  -> [row(c)×4, column(c)×4+(row(c)×4:3)]
                           (i.e. quarter steps down and right from a point a quarter
                                 left of the top-left of Q required to reach the bottom
                                 right of each of [A, B])
             /        - reduce by:
            ạ         -   absolute difference -> [vertical, horizontal] quarter steps
              ×4.76   - multiply by 4.7625 -> [vertical, horizontal] distances
                   ÆḊ - vector norm -> distance between keys

* Here is a suite showing all values are within \$0.1\$mm of the actual values - checking that all possible pairs using a quarter-key length of \$4.76\$ are within \$0.1\$ of those calculated using a quarter-key length of \$4.7625\$. (All results are actually also within \$0.1\$mm of Matt Parker's \$2\$ decimal place values - see this)

\$\endgroup\$
8
  • \$\begingroup\$ Thanks @NickKennedy! \$\endgroup\$ Commented Aug 22, 2021 at 20:15
  • \$\begingroup\$ 4.762 reasonably falls within the required bounds, saving a byte! \$\endgroup\$
    – emanresu A
    Commented Aug 23, 2021 at 9:52
  • \$\begingroup\$ @emanresuA thanks, totally forgot about that part of the spec. Have used \$4.76\$ and have asked whether we must be within \$0.1\$ or need to produce values that round to the same as those given at one decimal place. \$\endgroup\$ Commented Aug 23, 2021 at 11:37
  • \$\begingroup\$ theres a builtin for the qwerty keyboard? \$\endgroup\$ Commented Aug 24, 2021 at 12:22
  • \$\begingroup\$ @htmlcoderexe I'm sure there could be more two-byte builtins for other things often used in code golf questions (see "Assorted Nilds" on the Atoms page of the wiki). \$\endgroup\$ Commented Aug 24, 2021 at 15:36
7
\$\begingroup\$

R, 145 142 138 125 124 102 100 bytes

Edit: -4 bytes thanks to ovs

function(x,j=2-sapply(x,regexpr,'lo,kimjunhybgtvfrcdexswzaq'))dist(cbind(y<-j%%3,j%/%3+2^y/4))*19.05

Try it online!

Ungolfed

qwertydist=function(x){     # x = input = vector of two characters
 k='lo,kimjunhybgtvfrcdexswzaq'
                            # k = 26-character vector of keys of each keyboard column exept last (in reverse).
 j=2-sapply(x,regexpr,k)    # j = indices of x in reversed k, & offset by a fixed value so that the missing 'p' ends-up in the right place
 y<-j%%3                    # y = row of each key
 z=j%/%3+2^y/4              # z = col of each key, adjusted +.25 for row 1 & +.5 for row 2
 d=dist(cbind(y,z))*19.05   # d = euclidean distance between keys, in millimetres
}
\$\endgroup\$
2
  • \$\begingroup\$ If you offset all keys by the same constant, the distances stay the same, so j%%10+2^y/4 works instead of j%%10+(2^y-1)/4. \$\endgroup\$
    – ovs
    Commented Aug 23, 2021 at 8:40
  • \$\begingroup\$ @ovs - Yes, of course (!), thanks very much! \$\endgroup\$ Commented Aug 23, 2021 at 8:42
6
\$\begingroup\$

Python 3.8 (pre-release), 108 97 bytes

lambda x,y:abs(p(x)-p(y))*4.76
p=lambda c:(a:='.LO,KIMJUNHYBGTVFRCDEXSWZAQ'.find(c))%3*4j+a--a//3

Try it online!

p maps a character to a complex number corresponding to its key's position, at a scale where the key size is 4; the magnitude of the difference of p of the given characters, rescaled, gives the answer.
find returning -1 when the substring is not found is used to handle P.

-2 using the suggestion of a decimal multiplier from emanresu A, taken one step further – with the value 4.76, the maximum error is slightly over 0.09, just within the limit.
-9 by transposing the keyboard and making some other changes with that.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ 4.762 instead of 381/80 falls reasonably within the required bounds - Try it online! \$\endgroup\$
    – emanresu A
    Commented Aug 23, 2021 at 9:50
6
\$\begingroup\$

Vyxal , 22 bytes

ƛk•$Þḟ÷Ȯ⇩E+";ƒ∆d19.05*

Try it Online!

I still don't particularly like having to include the full 19.05, but at least this is a lot shorter. Vyxal outputs symbolic expressions for irrationals by default, so the flag turns them into decimals.

ƛ           ;          # Over both characters
    Þḟ                 # Get the multidimensional index in
 k•$                   # [rows of qwerty keyboard]
          +            # Add to the x-coordinate
       Ȯ E             # 2 to the power of the y-coordinate
        ⇩              # Divided by 4
      ÷    "           # (and swap them, but that doesn't matter)
             ƒ∆d       # Take the difference
                19.05* # and multiply by 19.05
\$\endgroup\$
3
  • \$\begingroup\$ -²∑√ -> ∆d? \$\endgroup\$
    – pxeger
    Commented Aug 22, 2021 at 12:57
  • \$\begingroup\$ @pxeger Thanks! I forgot about that builtin. \$\endgroup\$
    – emanresu A
    Commented Aug 22, 2021 at 20:38
  • \$\begingroup\$ @Dingus Great, Vyxal... \$\endgroup\$
    – emanresu A
    Commented Aug 23, 2021 at 9:23
5
\$\begingroup\$

JavaScript (ES6),  114  113 bytes

Saved 1 byte thanks to @Shaggy

Expects (a)(b).

a=>b=>Math.hypot((g=c=>(x="WERTYUIOPASDFGHJKLZXCVBNM".search(c))-[y=x>8,39,73][y+=x>17]/4)(a)-g(b,Y=y),y-Y)*19.05

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Could you save a byte by dropping the Q from the string and replacing 9 & 18 with 8 & 17? \$\endgroup\$
    – Shaggy
    Commented Aug 23, 2021 at 1:30
  • \$\begingroup\$ @Shaggy I considered that, but for some reason I thought it was not working. It actually does. Thanks! \$\endgroup\$
    – Arnauld
    Commented Aug 23, 2021 at 8:56
4
\$\begingroup\$

Jelly, 23 bytes

ØQœi2*÷8Ʋ+¥\)ạ/ÆḊ×19.05

Try it online!

A monadic link taking a list of two characters and returning a float.

\$\endgroup\$
4
\$\begingroup\$

Charcoal, 60 56 bytes

I×¹⁹·⁰⁵₂ΣE²X↨EEθ⌕”&⌈‽↶ ›I✂TFsG�¦ιAo⊟↧”λ⎇ι﹪λ³⊘⊘÷⊗⊗⊕λ³±¹¦²

Try it online! Link is to verbose version of code. Takes input as a string of two lower case letters. Explanation: The compressed string contains the letters in a staggered arrangement. The vertical offset is simply the index modulo 3, while the horizontal offset is computed by dividing the index by 3 and rounding to the nearest multiple of 0.25. Pythagoras is then used to compute the final result.

         E²                             Loop twice (horizontally/vertically)
              Eθ                        Loop over input
                ⌕...λ                   Find char indices in compressed string
             E       ⎇ι                 Extract either
                       ﹪λ³              Vertical offset
                          ⊘⊘÷⊗⊗⊕λ³      Horizontal offset
           X↨                     ±¹¦²  Take the squared difference
        Σ                               Take the sum
       ₂                                Take the square root
 ×¹⁹·⁰⁵                                 Multiply by literal 19.05
I                                       Cast to string for implicit print
\$\endgroup\$
4
\$\begingroup\$

05AB1E, 33 28 27 bytes

-1 byte thanks to Kevin Cruijssen!

Builtins for multi-dimensional index and euclidean distance would be helpful. Takes input in lowercase.

vžVyδkZDŠkDŠÍo+‚}-nOt19.05*

Try it online!

Commented:

v               }   # iterate over each character y in the input
 žV                 # push ["qwertyuiop", "asdfghjkl", "zxcvbnm"]
   yδk              # for each row, find the index of the current char in it (-1 if not found)
      ZD            # get the maximum one (the only one not equal to -1) twice (call this x)
        Š           # rotate top 3 values on the stack (x [indices] x)
         k          # find the index of x in the indices (call this y)
          DŠ        # duplicate y and rotate top 3 values (y x y)
            Ío      # 2**(y-2)
              +     # add to x   (y x+2**(y-2))
               ‚    # pair up both values [y, x+2**(y-2)]

 -                  # take the element-wise differences
  nOt               # Euclidean norm (square, sum, square root)
     19.05*         # multiply by constant 19.05 (*1905/100 would be the same length with integer compression)
\$\endgroup\$
3
  • 4
    \$\begingroup\$ I'm surprised there's nOt a shorter way to take the Euclidean norm... \$\endgroup\$
    – Neil
    Commented Aug 24, 2021 at 9:15
  • \$\begingroup\$ @Neil that is perhaps the most elegant comment I've seen on stack exchange in years. \$\endgroup\$
    – corsiKa
    Commented Aug 25, 2021 at 5:17
  • 1
    \$\begingroup\$ -1 byte by changing the map+swap (ε...s) to a for-each (v...y), so you can remove the dump `. \$\endgroup\$ Commented Sep 1, 2021 at 14:35
3
\$\begingroup\$

Perl 5, 129 + 4 (-apl options) = 133 bytes

($x,$y,$u,$v)=map{$i=index+QWERTYUIOPASDFGHJKL_ZXCVBNM,$_;$==$i/10;$i%10+(0,1,3)[$=]/4,$=}@F;$_=19.05*sqrt(($x-$u)**2+($y-$v)**2)

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Ruby, 88 bytes

->a{p,q=a.map{|i|b=:_QAZWSXEDCRFVTGBYHNUJMIK_OL_P=~/#{i}/
b*4/3+-b%3*4i}
(p-q).abs*4.76}

Try it online!

Used a symbol instead of a string as suggested by Dingus. Also improved formula for converting b to a complex number (saved several bytes, although it requires another dummy character at the start of the string.)

Ruby, 95 bytes

->a{p,q=a.map{|i|b='QAZWSXEDCRFVTGBYHNUJMIK<OL>P'.index i
b/3*4+b%3*3/2+b%3*4i}
(p-q).abs*4.76}

Try it online!

I used a QAZ WSXorder because it splits into rows and columns by the application of b/3 and b%3, saving three bytes over the alternative b/10 and b%10. p and q contain the coordinates as complex numbers, expressed as integer multiples of a quarter of a key. At the end we multiply by 4.76 which is in approximation of 19.05/4 = 4.7625. The value 4.76*4 = 19.04 gives an error of 0.01mm between two adjacent keys on the same row, giving a maximum error of 0.09mm between P and Q which is within allowed limits.

\$\endgroup\$
0
2
\$\begingroup\$

JavaScript (Node.js), 109 bytes

a=>b=>Math.hypot(g(a)-g(b,q=p),p-q)*4.7625
g=c=>8>>(p="QA ZWS XED CRF VTG BYH NUJ MIK ,OL .P".search(c))%4&12

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Per various other answers, 4.76 just barely falls within the required precision bounds. \$\endgroup\$
    – emanresu A
    Commented Sep 12 at 9:05
2
\$\begingroup\$

Perl 5 -pla, 121 117 bytes

@emanresuA's suggestion saved 4 bytes

QWERTYUIOPASDFGHJKL_ZXCVBNM=~/$_/g;push@x,2**($y[@y]=0|"@-"/10)/4+"@-"%10}{$"='-';$\=19.05*sqrt eval"(@x)**2+(@y)**2"

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ I don't know Perl but using 2**y or equivalent instead of (0,1,3) might save a few bytes? (maps to 1,2,4 instead but they're both shifted so it's fine) \$\endgroup\$
    – emanresu A
    Commented Sep 12 at 9:04
1
\$\begingroup\$

Uiua, 61 characters

×4.76⌵/ℂ-∩(+×4⟜(⊂0ⁿ:2⊢)⊢⊚=↯3_10"QWERTYUIOPASDFGHJKL-ZXCVBNM")

Try it here!

Thanks to emanresu A for -4 due to their tip on the perl -pla answer!

Contains the apparently standard techniques of deliberate error and complex numbers (inspired by the Uiua tips page).

\$\endgroup\$

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.