27
\$\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
    Aug 22 at 10:55
  • 7
    \$\begingroup\$ Well my keyboard only has the letter P, so the example keyboard is clearly wrong :P \$\endgroup\$ Aug 22 at 11:08
  • 4
    \$\begingroup\$ My keyboard doesn't even require Pythagoras! \$\endgroup\$ Aug 22 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\$ Aug 23 at 11:14
  • \$\begingroup\$ Thanks, I've updated the specification accordingly. \$\endgroup\$ Aug 23 at 16:25

10 Answers 10

9
\$\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\$ Aug 22 at 20:15
  • \$\begingroup\$ 4.762 reasonably falls within the required bounds, saving a byte! \$\endgroup\$
    – emanresu A
    Aug 23 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\$ Aug 23 at 11:37
  • \$\begingroup\$ theres a builtin for the qwerty keyboard? \$\endgroup\$ Aug 24 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\$ Aug 24 at 15:36
6
\$\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
    Aug 23 at 8:40
  • \$\begingroup\$ @ovs - Yes, of course (!), thanks very much! \$\endgroup\$ Aug 23 at 8:42
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
    Aug 23 at 1:30
  • \$\begingroup\$ @Shaggy I considered that, but for some reason I thought it was not working. It actually does. Thanks! \$\endgroup\$
    – Arnauld
    Aug 23 at 8:56
5
\$\begingroup\$

Vyxal, 39 bytes

ƛ£k•ƛ¥v=;:vaThD£ɾ0p∑4/∇iTh+¥";÷∆d19.05*

Try it Online! -2 thanks to pxeger. +2 because of a bug

Ugh, what a mess.

Vyxal has several bugs right now, so the find command isn't working, among other things.

Takes a two-char string of lowercase letters.

ƛ                          ;            # Map...
 £                                      # Store to register
  k•ƛ   ;                               # Map rows of qwerty keyboard to...
     ¥v=                                # Is equal to register (current value)
         :                              # Duplicate this
          va                            # Foreach, check if any are true
            Th                          # Find the only truthy index - this is the correct keyboard row
              D£                        # Duplicate and store a copy to register 
                ɾ0p∑4/                  # Horizontal offset of this row - nth triangle number/4 (There's probably a better way to do this, `0p` because Vyxal)
                      ∇                 # Shift, putting a copy of the  row number above the list of indices on the stack
                       i                # Index this into the list of indices
                        Th+             # Find the index of the key and add to previously calculated horizontal offset
                           ¥"           # Pair with vertical offset
                              ÷         # Iterate onto the stack
                               ∆d       # Pythagoras' Theorem
                                 19.05  # Multiply by 19.05
\$\endgroup\$
3
  • \$\begingroup\$ -²∑√ -> ∆d? \$\endgroup\$
    – pxeger
    Aug 22 at 12:57
  • \$\begingroup\$ @pxeger Thanks! I forgot about that builtin. \$\endgroup\$
    – emanresu A
    Aug 22 at 20:38
  • \$\begingroup\$ @Dingus Great, Vyxal... \$\endgroup\$
    – emanresu A
    Aug 23 at 9:23
5
\$\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
    Aug 23 at 9:50
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
    Aug 24 at 9:15
  • \$\begingroup\$ @Neil that is perhaps the most elegant comment I've seen on stack exchange in years. \$\endgroup\$
    – corsiKa
    Aug 25 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\$ Sep 1 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

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.