Clarence the Slow Typist

35

4

Introduction

Clarence is a data entry clerk who works at an internet service provider. His job is to manually enter the IP addresses of all of the ISP's customers into the database. He does this using a keypad which has the following layout:

123
456
789
.0

The distance between the centre of horizontally or vertically adjacent keys is exactly one centimetre. For instance, the distance between the centres of 3 and 9 would be two centimetres. The distance between the centres of 3 and 5 would be √2cm. The Pythagoras theorem is sufficient to calculate the distance between any two keys.

Clarence, as you might expect from one who works in an ISP, uses a very slow and inefficient system of typing. He uses a single finger and searches for the key, and then moves his finger to the key, then presses it, and repeats for all of the digits in the number. You might know of this style as the "eagle search system" since the finger searches above the keyboard for the correct key before plunging down for the keypress, like an eagle plunging down for a kill.

For example, here is how Clarence would type out the number 7851:

  1. He starts his finger at 7 and pushes the key.
  2. He moves his finger to the right 1cm to 8 and pushes the key.
  3. He moves his finger upwards 1cm to 5 and pushes the key.
  4. He moves his finger diagonally upwards and left √2cm to 1 and pushes the key.

Therefore the total distance that Clarence moved his finger to type in 7851 is 1 + 1 + √2 which is about 3.41cm.

Your task is to write a program that calculates the distance Clarence must move his finger to type in arbitrary IP addresses.

Input Description

Input is a string that will be in the form

().().().()

where each () is an integer in the range 0 - 999. This represents the IP address that Clarence must type in. An example input might be:

219.45.143.143

I would also like to point out that inputs such as 0.42.42.42 or 999.999.999.999 are still valid inputs, despite the fact that they are invalid IP addresses. So you don't need to include any IP address verification code in your program.

Output Description

Output the distance that Clarence must move his finger in order to type in the specified IP address. Round answers to two decimal places where needed, and use the cm unit in your output. The output for the example input is 27.38cm (1 + √8 + √5 + 2 + 1 + √5 + 3 + 1 + √5 + √13 + 3 + 1 + √5).

absinthe

Posted 2015-05-25T01:59:03.873

Reputation: 8 359

29Man, ISPs have weird keyboards... – Dennis – 2015-05-25T02:15:11.517

I wonder if anyone's entry will actually make use of the dotted quad format, or if most/all will just accept any arbitrary string of [0-9.]+ – Sparr – 2015-05-25T03:20:51.943

@Sparr: At least it excludes the possibility of a one- character input, which would be real pain in CJam. – Dennis – 2015-05-25T05:30:28.297

Can you clarify what forms of input are allowed? Since you say "program", does this imply that you expect a full program that takes input from stdin or command line arguments? Or is a function that takes a string as argument also acceptable? – Reto Koradi – 2015-05-25T05:57:08.430

1@RetoKoradi I expect a program, yes. stdin, command line arguments, or user input functions are acceptable. – absinthe – 2015-05-25T05:59:07.813

2@dacapoaria - 'eagle search' is also known as 'hunt and peck' or 'search and destroy' for the more heavy-handed typists. – None – 2015-05-25T09:51:33.733

1@MathiasFoster Search and Destroy typing gives me an interesting mental image. Thanks for telling me these different terms. – absinthe – 2015-05-25T10:45:45.017

Why 0-999 when IPs can only be 0-255? – ArtOfCode – 2015-05-25T11:50:29.213

12@ArtofCode Clarence works at an ISP, and sometimes the ISP sends him the invalid data to type into the database. Clarence types the data in anyway. That's the canonical reason anyway. The actual reason is because I overlooked that when writing the spec. – absinthe – 2015-05-25T11:59:29.750

3Considering only the valid range (0-255) IP Addresses, which should be the optimum arrangement of the keyboard to type all that addresses in the shortest path? – Israel Morales – 2015-05-25T17:18:26.000

1@IsraelMorales Hmmm... my hunch would be that you'd have the . in the middle, 2, 5, 1, and 3 adjacent, 9 and 8 outside of the square and the remaining ones in the corners. That's just my guess though. – absinthe – 2015-05-26T00:25:23.953

Answers

16

CJam, 46 44 43 38 37 34 bytes

rA,sd`f#3fmd2/2ew::.-::mh:+2mO"cm"

Thanks to @user23013 for suggesting mh, which made it possible to save 5 bytes.

Try it online in the CJam interpreter.

How it works

r     e# Read a token from STDIN.
A,    e# Push [0 1 2 3 4 5 6 7 8 9].
s     e# Stringify: [0 1 2 3 4 5 6 7 8 9] -> "0123456789"
d     e# Cast to Double: "0123456789" -> 123456789.0
`     e# Inspect: 123456789.0 -> "123456789.0"
f#    e# Push the index of each character from the input in "123456789.0".
3fmd  e# Push the quotient and residue of each index divided by 3.
2/    e# Split the resulting array into pairs.
2ew   e# Convert the array of pairs in the array of all overlapping pairs of pair.
::.-  e# Reduce each pair using vectorized difference: [[a b][c d]] -> [a-b c-d]
::mh  e# Reduce each reduced pair to its 2-norm distance: [a b] -> sqrt(aa + bb)
:+    e# Sum all distances.
2mO   e# Round to two decimal places.
"cm"  e# Push "cm".

Dennis

Posted 2015-05-25T01:59:03.873

Reputation: 196 637

2{3fmd~@-@@-mh}%. – jimmy23013 – 2015-05-25T03:57:28.167

@user23013: Thanks. I had no idea mh even existed. – Dennis – 2015-05-25T04:16:10.093

16

Pyth, 38 35 34 bytes

+.Rs.aM-M.:m.jF.Dx`ciUTT1b3z2 2"cm

Demonstration.

Indexing into the string of a float idea thanks to @Dennis.

Explanation, on the phony input 15.0:

  • First, we take the input. It is implicitly stored in z. '15.0'
  • We map this list as follows: m.jF.Dx`ciUTT1k3z.

    • UT: We generate the list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].
    • iUTT: Next, we treat this list as a base 10 number giving us 123456789.
    • ciUTT1: Next, we convert this number to a float by floating point dividing it by 1, giving 123456789.0.
    • `: Convert to a string. '123456789.0'
    • x k: Take the index of the input character in that string. [0, 4, 9, 10].
    • .D 3: .D is the divmod function, outputing its first input divided and modulo'd by the second input. The second input is 3, here. This gives the physical location of the character on the numpad. [(0, 0), (1, 1), (3, 0), (3, 1)].
    • .jF: .j is the complex number constructor. F applies it to the tuple. [0j, (1+1j), (3+0j), (3+1j)].
  • .: 2: Now, we take the 2 entry substrings of this list so we can find the pairwise distances. [[0j, (1+1j)], [(1+1j), (3+0j)], [(3+0j), (3+1j)]].
  • -M: Takes the difference of the two complex numbers. [(-1-1j), (-2+1j), -1j].
  • .aM: Takes the absolute value of the result. This is the distance between the keypad locations. [1.4142135623730951, 2.23606797749979, 1.0]
  • s: Sum the distances. 4.650281539872885.
  • .R 2: Round to 2 decimal places. 4.65.
  • + "cm: Add 'cm' to the end and print. 4.65cm.

isaacg

Posted 2015-05-25T01:59:03.873

Reputation: 39 268

7

PHP - 108 Bytes

<?for(;$v=strpos(-.987654321,fgetc(STDIN));$l=$v)$l&&$t+=hypot($l/3%4-$v/3%4,$l%3-$v%3);printf('%.2fcm',$t);

Input is taken from stdin. The -.987654321 sent to the strpos function evaluates to '-0.987654321' in a string context.


Sample usage:

$ echo 219.45.143.143 | php isp.php
27.38cm

primo

Posted 2015-05-25T01:59:03.873

Reputation: 30 891

5

C, 192 177 159 bytes

Updated version, now complete program using command line argument. At the same time, improved to still be shorter than previous version:

#define G c=*a[1]++,c=c>48?c-49:c/2-14,u=c%3,v=c/3
float r;c,u,v,p,q;main(int n,char**a){for(G;*a[1];)p=u,q=v,G,p-=u,q-=v,r+=sqrt(p*p+q*q);printf("%.2fcm",r);}

Ungolfed:

#include <stdio.h>
#include <math.h>

float r;
int c, u, v, p, q;

int main(int n, char** a) {
    c = *a[1]++;
    c = c > 48 ? c - 49 : c / 2 - 14;
    u = c % 3;
    v = c / 3;
    for ( ; *a[1]; ) {
        p = u;
        q = v;
        c = *a[1]++;
        c = c > 48 ? c - 49 : c / 2 - 14;
        u = c % 3;
        v = c / 3;
        p -= u;
        q -= v;
        r += sqrt(p * p + q * q);
    }

    printf("%.2fcm",r);

    return 0;
}

The golfed version uses a preprocessor #define to shorten some of the repeated code in the full version.

Reto Koradi

Posted 2015-05-25T01:59:03.873

Reputation: 4 870

2>

  • Your golfed version is missing a semicolon at the end. 2. Your golfed version is producing incorrect results, since you increment s before checking that *s is non-zero. 3. The OP said program in his post. I'm not sure if a function is accepted. 4. With GCC, you don't need the include. 5. sqrt is shorter than sqrtf. 6. pow(u-p,2) is shorter than (u-p)*(u-p). 7. I'm not sure, but I think storing both coordinates in a single string and setting u=x[c]/3 and v=x[c]%3 should be shorter.
  • < – Dennis – 2015-05-25T04:59:34.680

    Fixed the correctness problem. Turns out that I kept compiling an earlier version while doing fine tuning. Sorry about that. 1, 2. Fixed. I was actually surprised that I could leave them away. The broken testing would explain it... 3. Based on what I saw on wiki/meta, it sounded like taking the input as function arguments is an allowed option if the input is not explicitly specified. I'll change it if my interpretation is incorrect. 4. I always thought that only functions that return int could be used undeclared. But indeed, clang also accepts it with a warning, so I got rid of it. – Reto Koradi – 2015-05-25T05:38:18.350

    The wiki states that functions are allowed by the default, yes, but the OP wrote your task is to write a program... You don't need the brackets you introduced if you write the loop as p=u,q=v,G,r+=.... – Dennis – 2015-05-25T05:52:36.540

    I asked the OP for clarification on the input requirements. On the code, I rolled it back to a slightly older version before I optimized it into incorrectness. I'll take another shot at tuning it tomorrow, but I didn't want to leave a broken version standing for too long. Thanks for the pointers. – Reto Koradi – 2015-05-25T06:01:06.400

    @Dennis Ok, updated version should be better in every way. Now a complete program, and still shorter thanks to some optimizations. Thanks again for letting me know about the problems with the initial version. – Reto Koradi – 2015-05-25T22:19:07.393

    3

    JavaScript (ES6), 132

    I/O via popup. Run the snippet to test (Firefox only)

    [for(a of prompt(d=''))[p,q,d]=[x=(a=a<'0'?9:-a?a-1:10)%3,y=a/3|0,d!==''?d+Math.sqrt((p-=x)*p+(q-=y)*q):0]],alert(d.toFixed(2)+'cm')

    edc65

    Posted 2015-05-25T01:59:03.873

    Reputation: 31 086

    3

    Python 3, 108 bytes

    L=[x//3*1j+x%3for x in map("123456789.0".find,input())]
    print("%.2fcm"%sum(abs(a-b)for a,b in zip(L,L[1:])))
    

    Admittedly not very well golfed, but at least it ties with PHP.

    Sp3000

    Posted 2015-05-25T01:59:03.873

    Reputation: 58 729

    2

    Ruby 135 139

    s=0
    gets.chars.each_cons(2).map{|a|i,j=a.map{|e|'123456789.0'.index e}
    i&&j&&s+=((i%3-j%3)**2+(i/3-j/3)**2)**0.5}
    print s.round(2),'cm'
    

    Test it online: http://ideone.com/2CIQa5

    Cristian Lupascu

    Posted 2015-05-25T01:59:03.873

    Reputation: 8 369

    2

    Python 199 171 166

    There's a shorter Python code (108) for this by SP3000:

    https://codegolf.stackexchange.com/a/50854/41163

    import sys
    p=r=0
    for i in sys.argv[1]:
     c=3,0
     if i!=".":c=divmod(int(i)-1,3)
     if i<1:c=3,1
     if p:r+=((p[1]-c[1])**2+(p[0]-c[0])**2)**0.5
     p=c
    print"%.2fcm"%r
    

    Sample usage:

    $ python isp.py 219.45.143.143
    27.38cm
    

    Run online: http://codepad.org/h9CWCBNO

    Commented code

    import sys
    
    # p - last position as (y,x) tuple - initialized with 0, because "if 0" -> equals to "False"
    p = 0
    # r - result of adding all distances - ini with 0, because not moved any distances on start
    r = 0
    
    # Loop over chars
    for char in sys.argv[1]:
       # c - current position of typist as (y,x) tuple
    
       # Always set c to position of "." key 
       c = 3,0 # lazy for c=(3,0)
    
       # Check if char is not the "." key
       if char !=".":
    
          # Get position of char on keypad
          c=divmod(int(char)-1,3)
    
          if char<1:
             c=3,1  
    
       # If this is the first char, 
       # then STORE_OPERATION has not been executed, 
       # so p is still p=0 from original initialization 
       # calling "if 0" evaluates to False,
       # so we jump this code block, for the first char
       if p:                           
          # calculate delta of x, y from current and last position, 
          # then add both deltas squared (**2),
          # then get square root of it (**0.5 = **1/2)
          # add to r (+=)
          r+=( (p[1]-c[1])**2 + (p[0]-c[0])**2 )**0.5
    
       # STORE_OPERATION - Store current position as last position
       p = c
    
    # .2f returns r with 2 trailing digits
    print"%.2fcm"%r
    

    AddingColor

    Posted 2015-05-25T01:59:03.873

    Reputation: 121

    1You can save some bytes by defining if clauses on one line, like if i<1:c=3,1 – Zgarb – 2015-05-27T13:08:53.337

    1You can add syntax highlighting by putting this comment at the top of your post: <!-- language: lang-python --> – Martin Ender – 2015-05-27T13:09:24.423