Distance to four

This challenge is based on this video. I recommend that you watch it before trying this challenge.

First we define a function. This function (OEIS) takes a integer n as input and outputs the number of letters in the English representation of n (without spaces or hyphens). For example "three" has 5 letters so 3 maps to 5.

As demonstrated in the video starting with any number repeating this process will eventually result in a four, which will map to itself forever.

Here is a crude directed graph showing the orbits of the numbers less than 16:

12 11
\|
15 2 6 1 10 14 13
\ \|/ /  /  /
7 3-/  8--/
\|   /
9 0 5--/
\ \|
\-4

Your challenge is to determine the number of steps that a number will take (or the number of times this function must be applied to a number) before reaching four (i.e. the level on the directed graph pictured).

Forming english numbers

Here is a brief explanation of how one should form english words for this challenge:

The numbers one through nineteen are:

one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen

For numbers greater than nineteen the process is as follows:

If the number has a hundreds place begin with the name of digit in the hundreds place and "hundred".

e.g.

100 -> "onehundred"

If the remainder is less than twenty append the remainder's English representation.

e.g.

714 -> "sevenhundredfourteen"

Otherwise if the tens digit is not zero append the proper representation:

2-> twenty
3-> thirty
4-> forty
5-> fifty
6-> sixty
7-> seventy
8-> eighty
9-> ninety

e.g.

470 -> "fourhundredseventy"

Finally if there is a one's digit append its representation

e.g.

681 -> "sixhundredeightyone"

Further Stipulations

• For numbers greater than one hundred you should leave out the "and" when counting the number of letters. For instance 577 is "fivehundredseventyseven" which has 23 letters.

• Your program must accept all integers greater than 0 and less than 1,000 as input via standard methods.

• Your program must output the number of steps required to standard output methods.

• This is codegolf so the solution with the fewest bytes wins.

Test cases

1 -> 3
4 -> 0
7 -> 2
23 -> 5
577 -> 6
600 -> 4
• Related I thought this was a dupe, but I can't find it. – DJMcMayhem Sep 10 '16 at 16:51
• What happened to "and"? Or rather, why leave out and?! – Jonathan Allan Sep 10 '16 at 19:17
• @JonathanAllan cuz 'Murica – LegionMammal978 Sep 10 '16 at 20:18

JavaScript (ES6), 106 bytes

f=(n,a="03354435543668877998")=>n-4&&1+f(7*(n>99)-(-a[n/100|0]-(a[n%=100]||a[n%10])-"0066555766"[n/10|0]))

Strings seem to be the best way to encode the length, despite the numeric conversion overhead.

• Gosh, that's almost exactly what my (nearly-posted) answer looked like, only 11 bytes shorter. – ETHproductions Sep 10 '16 at 21:10
• @ETHproductions Good thing I golfed 16 bytes off before posting it then! – Neil Sep 10 '16 at 21:53

Python, with num2words, 97 113 115 94 93 92 bytes

+16 bytes (forgot the hyphenation that num2words applies which does not actually change the results of any of the test cases, even though 23 and 577 each have a hyphen)
+2 bytes (forgot to include f= although recursive)
-20 bytes (use re)
-8 bytes thanks to @Wheat Wizard (use ~, replace n!=4 with n-4, and ...one line import >_<)
-1 byte thanks to @Cyoce (space from 4 and)

import re,num2words as w
f=lambda n:n-4and-~f(len(re.sub('\W|and','',w.num2words(n))))

Just counts up the number of steps; works for huge and negative integers too (\W finds the spaces, commas and hyphens in the num2words result):

>>> for test in (1,4,7,23,577,600,-1*2**96,3**96):
...     print('test: {0}  ->  {1}'.format(test, f(test)))
...
test: 1  ->  3
test: 4  ->  0
test: 7  ->  2
test: 23  ->  5
test: 577  ->  6
test: 600  ->  4
test: -79228162514264337593543950336  ->  4
test: 6362685441135942358474828762538534230890216321  ->  5

Here is that last case, step by step:

fourhundredfiftyone
nineteen
eight
five
• Don't you need a f= before your lambda function – Wheat Wizard Sep 10 '16 at 20:00
• Try import re,num2words as r instead of the two different statements. – Wheat Wizard Sep 10 '16 at 20:11
• n-4 is the same thing as n!=4 – Wheat Wizard Sep 10 '16 at 20:14
• @WheatWizard num2words is w, re is still re - note that both the module and function are called num2words – Jonathan Allan Sep 10 '16 at 20:18
• Ok last one, and 1+ can be replaced with and-~ to save one byte – Wheat Wizard Sep 10 '16 at 20:22

Pyth - 54 bytes

Will try to refactor.

Mathematica, 89 bytes

Length@FixedPointList[StringLength@StringReplace[IntegerName@#,{" "->"","-"->""}]&,#]-2&

Typical Mathematica: built-in functions good, long function names bad. FixedPointList applies its first argument (a function) to the second argument repeatedly until the answer doesn't change, listing all results; the results include the original input and two copies of the repeated output, hence the -2 at the end. Mathematica's built-in IntegerName contains spaces and hyphens, so we need to get rid of those by hand.

Annoyingly, IntegerName's output contains the character "‐" (Unicode #8208) rather than normal hyphens; that's why this submission is 89 bytes rather than 88. (And I couldn't precede the code above with four spaces and have it accept the Unicode character—any help?—so the above code won't work exactly right if cut and pasted.)

Python 2.7, 344216 208 bytes:

x=input();c=0;y=lambda v:dict(zip(range(0,10),+v));l=[3,3,5,4,4,3,5,5,4];d=y(l);e=y([3,6,6,6,5,5,7,7,6]);f=y([i+7for i in l])
while x!='4':x=sum([q[int(r)]for q,r in zip([d,e,f],x[::-1])]);c+=1
print c

Does not use any external libraries unlike other Python answer. Takes input through stdin and outputs to stdout.

Repl.it with All Test Cases!

Explanation

First creates 3 dictionaries with each one pairing the length of the English word representations of each number to the number it represents in the closed interval [1,9] in the ones, tens, and hundreds place, respectively. For example, the first entry in dictionary d is 1:3 as 1 is spelled one in English and has 3 letters.

Then, each digits place in some string input x is assigned to its corresponding dictionary, after which each number in each place is matched up with its value in the corresponding dictionary. For instance, suppose the input number was 23. The 20 in the tens place would be paired with dictionary e, in which it is matched with 6, and the 3 in the ones place would be paired with dictionary d, in which it is matched with 5. These matched digits are then added together to represent the length of the English representation of the number, which is assigned to x as a string and, as long as x!='4', the while loop goes on, incrementing c by 1 each time to represent the number of steps taken thus far. Therefore, 23 would correspond to 11, which would in turn correspond to 6 which would turn to 3 and then to 5 and finally to 4, resulting in 5 total steps.

Finally, once the loop finishes, c is output to stdout to represent the "Distance to Four", which in this case would be 5.

Java, 556 295 bytes

Thanks to @KevinCruijssen for saving 261 bytes

void int(n) {int s[]={0,3,3,5,4,4,3,5,5,4,3,6,6,8,8,7,7,9,9,8,6,9,9,11,10,6,6,5,5,7,7,6};int c=0,t=(int)Math.pow(10,(int)Math.log10(n)),v=1;while(n>0){if(n/100>0)c+=(s[n/100]+7);else {if(n>0&n<25){c+=s[n];break;}else{c+=s[(n/10)+22];}}n=n%t;t=t/10;}while(c!=4){v++;c=s[c];}System.out.print(v);}

Ungolfed:

void int(n) {

int s[]={0,3,3,5,4,4,3,5,5,4,3,6,6,8,8,7,7,9,9,8,6,9,9,11,10,6,6,5,5,7,7,6};
int c=0,t=(int)Math.pow(10,(int)Math.log10(n)),v=1;
while(n>0){
if(n/100>0)
c+=(s[n/100]+7);
else {if(n>0&n<25){
c+=s[n];
break;
}
else{
c+=s[(n/10)+22];

}
}
n=n%t;
t=t/10;
}

while(c!=4)
{
v++;
c=s[c];
}
System.out.print(v);
}
• I think you have a mistake in your code, because s++ isn't possible on a String-array.. :S – Kevin Cruijssen Sep 12 '16 at 7:46
• @KevinCruijssen I declare S(counter) as a INT and STRING too....java automatically decide it is an INT. – Numberknot Sep 12 '16 at 7:51
• Well, if I run your code in ideone or my Eclipse IDE it fails because you have two s.. Btw, you can golf your code by quite a large amount like this: int c(int n){int s[]={0,3,3,5,4,4,3,5,5,4,3,6,6,8,8,7,7,9,8,8,6,9,9,11,10,6,5,5,5,7,6,6},c=0,t=(int)Math.pow(10,(int)Math.log10(n)),x=1;while(n>0)if(n/100>0)c+=s[n/100]+7;else{if(n>0&n<25){c+=s[n];break;}else c+=s[(n/10)+22];}n%=t;t/=10;}for(;c!=4;x++,c=s[c]);return x;} – Kevin Cruijssen Sep 12 '16 at 7:59
• I am shocked......what happen to my code and thanks @KevinCruijssen..and i am correcting it...Thanks again. – Numberknot Sep 12 '16 at 8:12
• Np :) I think it might be golfed some more somehow without using the if-else and break in there (but I'll leave that to someone else), but your initial code was a great base for tackling the challenge, so +1 from me. – Kevin Cruijssen Sep 12 '16 at 9:19