35
\$\begingroup\$

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).

\$\endgroup\$
  • 29
    \$\begingroup\$ Man, ISPs have weird keyboards... \$\endgroup\$ – Dennis May 25 '15 at 2:15
  • 1
    \$\begingroup\$ @RetoKoradi I expect a program, yes. stdin, command line arguments, or user input functions are acceptable. \$\endgroup\$ – absinthe May 25 '15 at 5:59
  • 2
    \$\begingroup\$ @dacapoaria - 'eagle search' is also known as 'hunt and peck' or 'search and destroy' for the more heavy-handed typists. \$\endgroup\$ – user31556 May 25 '15 at 9:51
  • 12
    \$\begingroup\$ @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. \$\endgroup\$ – absinthe May 25 '15 at 11:59
  • 3
    \$\begingroup\$ Considering 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? \$\endgroup\$ – Israel Morales May 25 '15 at 17:18
16
\$\begingroup\$

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".
\$\endgroup\$
  • 2
    \$\begingroup\$ {3fmd~@-@@-mh}%. \$\endgroup\$ – jimmy23013 May 25 '15 at 3:57
  • \$\begingroup\$ @user23013: Thanks. I had no idea mh even existed. \$\endgroup\$ – Dennis May 25 '15 at 4:16
16
\$\begingroup\$

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.
\$\endgroup\$
7
\$\begingroup\$

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
\$\endgroup\$
5
\$\begingroup\$

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ 1. 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. \$\endgroup\$ – Dennis May 25 '15 at 4:59
  • \$\begingroup\$ 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. \$\endgroup\$ – Reto Koradi May 25 '15 at 5:38
  • \$\begingroup\$ 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+=.... \$\endgroup\$ – Dennis May 25 '15 at 5:52
  • \$\begingroup\$ 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. \$\endgroup\$ – Reto Koradi May 25 '15 at 6:01
  • \$\begingroup\$ @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. \$\endgroup\$ – Reto Koradi May 25 '15 at 22:19
3
\$\begingroup\$

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')

\$\endgroup\$
3
\$\begingroup\$

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.

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

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

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

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
\$\endgroup\$
  • 1
    \$\begingroup\$ You can save some bytes by defining if clauses on one line, like if i<1:c=3,1 \$\endgroup\$ – Zgarb May 27 '15 at 13:08
  • 1
    \$\begingroup\$ You can add syntax highlighting by putting this comment at the top of your post: <!-- language: lang-python --> \$\endgroup\$ – Martin Ender May 27 '15 at 13:09

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.