Welcome to the Monastery PerlMonks

### Code Golf: Four is magic

by deMize (Monk)
 on Jul 12, 2010 at 23:20 UTC Need Help??

Question: Originally found here: http://stackoverflow.com/questions/3230978/code-golf-four-is-magic, I'm curious if anyone here could do better

### The puzzle

A little puzzle I heard while I was in high school went something like this...

• The questioner would ask me to give him a number;
• On hearing the number, the questioner would do some sort of transformation on it repeatedly (for example, he might say ten is three) until eventually arriving at the number 4 (at which point he would finish with four is magic).
• Any number seems to be transformable into four eventually, no matter what.

The goal was to try to figure out the transformation function and then be able to reliably proctor this puzzle yourself.

### The solution

The transformation function at any step was to

• Take the number in question,
• Count the number of letters in its English word representation, ignoring a hyphen or spaces or "and" (e.g., "ten" has 3 letters in it, "thirty-four" has 10 letters in it, "one hundred forty-three" has 20 letters in it).
• Return that number of letters.

For all of the numbers I have ever cared to test, this converges to 4. Since "four" also has four letters in it, there would be an infinite loop here; instead it is merely referred to as magic by convention to end the sequence.

### The challenge

Your challenge is to create a piece of code that will read a number from the user and then print lines showing the transformation function being repeatedly applied until "four is magic" is reached.

Specifically:

1. Solutions must be complete programs in and of themselves. They cannot merely be functions which take in a number-- factor in the input.
2. Input must be read from standard input. (Piping from "echo" or using input redirection is fine since that also goes from stdin)
3. The input should be in numeric form.
4. For every application of the transformation function, a line should be printed: a is b., where a and b are numeric forms of the numbers in the transformation.
5. Full stops (periods) ARE required!
6. The last line should naturally say, 4 is magic..
7. The code should produce correct output for all numbers from 0 to 99.

Examples:

> 4
4 is magic.

> 12
12 is 6.
6 is 3.
3 is 5.
5 is 4.
4 is magic.

> 42
42 is 8.
8 is 5.
5 is 4.
4 is magic.

> 0
0 is 4.
4 is magic.

> 99
99 is 10.
10 is 3.
3 is 5.
5 is 4.
4 is magic.

The winner is the shortest submission by source code character count which is also correct.

### BONUS

You may also try to write a version of the code which prints out the ENGLISH NAMES for the numbers with each application of the transformation function. The original input is still numeric, but the output lines should have the word form of the number.

I'm considering these a separate category for bonus competition with regard to the challenge, so if you go for this, don't worry about your code being longer than the numeric version.

Feel free to submit one solution for each version.

Demize

Replies are listed 'Best First'.
Re: Code Golf: Four is magic
by duelafn (Vicar) on Jul 13, 2010 at 03:45 UTC

Well, I'm not much of a golfer, but here is a proof of concept (at a whopping 258 strokes) that goes up to 999 nonillion... using spelling in Lingua::EN::Numbers (limiting to 0..99 is too boring)

```# Update: 222 strokes by using a bareword and (for the fun of it) supp
+ort up to undecillion 10^36 (costs no extra chars due to bareword tri
+ck)
for f in 0 4 12 99 1024 1000024 999999999999999999999999999999999; do
+echo \$f | \
perl -E'@x=a33544355436688779880066555766=~/./g;\$_=<>;chop;while((\$a=\$
+_)-4){split//,b999aabb87780;\$x=4*!\$_;\$x+=hex(pop@_)*!!-\$&+!!\$1*(\$x[\$1
+]+7)+\$x[\$2+20]+\$x[\$3]while s/(.??)([^1]?)(1?.)\$//;say"\$a is \$x.";\$_=\$
+x}say"4 is magic."' ; \
echo; done

# Update: 224 strokes by including the suggested =~/./g shortcut and m
+oving output around a bit
perl -E'@x="033544355436688779880066555766"=~/./g;\$_=<>;chop;while((\$a
+=\$_)-4){split//,"99aabb87780";\$x=4*!\$_;\$x+=hex(pop@_)*!!-\$&+!!\$1*(\$x[
+\$1]+7)+\$x[\$2+20]+\$x[\$3]while s/(.??)([^1]?)(1?.)\$//;say"\$a is \$x.";\$_
+=\$x}say"4 is magic."'

# Update: 229 strokes by moving some conditionals into the regex
perl -E'@x=split//,"033544355436688779880066555766";\$_=<>;chop;print;w
+hile(\$_-4){split//,"99aabb87780";\$x=4*!\$_;\$x+=hex(pop@_)*!!-\$&+!!\$1*(
+\$x[\$1]+7)+\$x[\$2+20]+\$x[\$3]while s/(.??)([^1]?)(1?.)\$//;say" is \$x.";p
+rint\$_=\$x}say" is magic."'

# Update: Ah, ha! 239 strokes:  !!-\$& will do it
perl -E'@x=split//,"0335443554366887798866555766";\$_=<>;chop;print;whi
+le(\$_-4){split//,"99aabb87780";\$x=4*!\$_;\$x+=hex(pop@_)*!!-\$&+!!\$1*(\$x
+[\$1]+7)+(\$2>1?\$x[\$2+18]+\$x[\$3]:\$x[\$2.\$3])while s/(.??)(.?)(.)\$//;say"
+ is \$x.";print\$_=\$x}say" is magic."'

# Update: 242 strokes, and works :), stealing some ideas from haarg &
+thundergnat
perl -E'@x=split//,"0335443554366887798866555766";\$_=<>;chop;print;whi
+le(\$_-4){split//,"99aabb87780";\$x=4*!\$_;\$x+=hex(pop@_)*!!(0+\$&)+!!\$1*
+(\$x[\$1]+7)+(\$2>1?\$x[\$2+18]+\$x[\$3]:\$x[\$2.\$3])while s/(.??)(.?)(.)\$//;s
+ay" is \$x.";print\$_=\$x}say" is magic."'

# Update: Down to 258 strokes
perl -E'@x=split//,"0335443554366887798866555766";\$_=<>;chop;print;whi
+le(\$_!=4){split//,"99aabb87780";\$_?do{\$x=0;while(s/(.??)(.?)(.)\$//){\$
+x+=hex(pop@_)*(0+\$&?1:0)+(\$1?\$x[\$1]+7:0)+(\$2>1?\$x[\$2+18]+\$x[\$3]:\$x[\$2
+.\$3])}}:(\$x=4);say" is \$x.";print\$_=\$x}say" is magic."'

# 261 strokes:
perl -E'@x=split//,"0335443554366887798866555766";\$_=<>;chop;print;whi
+le(\$_!=4){@m=split//,"99aabb87780";\$_?do{\$x=0;while(s/(.??)(.?)(.)\$//
+){\$x+=hex(pop@m)*(0+\$&?1:0)+(\$1?\$x[\$1]+7:0)+(\$2>1?\$x[\$2+18]+\$x[\$3]:\$x
+[\$2.\$3])}}:(\$x=4);say" is \$x.";print\$_=\$x}say" is magic."'

999999999999999999999999999999999 is 321.
321 is 21.
21 is 9.
9 is 4.
4 is magic.

Good Day,
Dean

A couple small improvements:
```perl -E'@x=split//,"0335443554366887798866555766";\$_+=<>;print;while(\$
+_!=4){split//,"99aabb87780";\$_?do{\$x=0;\$x+=hex(pop)*!!\$&+(\$1&&\$x[\$1]+
+7)+(\$2>1?\$x[\$2+18]+\$x[\$3]:\$x[\$2.\$3])while s/(.??)(.?)(.)\$//}:(\$x=4);s
+ay" is \$x.";print\$_=\$x}say" is magic."'

That actually has problems with large numbers (> native integer ?) Once it gets over 64 bits on my machine it converts to scientific notation... which throws off the conversion.

Never mind. Broken

Here's my whack at it - 232 strokes

```perl -E'@x=split//,"4335443554366887798866555766";\$_=<>;chop;print;whi
+le(\$_-4){split//,"99aabb87780";\$x=0;\$x+=hex(pop)*!!\$&+(\$1&&\$x[\$1]+7)+
+(\$2>1?\$x[\$2+18]+\$x[\$3]:\$x[\$2.\$3])while s/(.??)(.?)(.)\$//;say" is \$x."
+;print\$_=\$x}say" is magic."'

__END__
999999999999999999999999999999999
999999999999999999999999999999999 is 231.
231 is 19.
19 is 8.
8 is 5.
5 is 4.
4 is magic.

A modest improvement on your 229:
227 strokes

```perl -E'@x="033544355436688779880066555766"=~/./g;\$_=<>;chop;print;whi
+le(\$_-4){split//,"99aabb87780";\$x=4*!\$_;\$x+=hex(pop@_)*!!-\$&+!!\$1*(\$x
+[\$1]+7)+\$x[\$2+20]+\$x[\$3]while s/(.??)([^1]?)(1?.)\$//;say" is \$x.";pri
+nt\$_=\$x}say" is magic."'
Re: Code Golf: Four is magic
by Limbic~Region (Chancellor) on Jul 13, 2010 at 00:09 UTC
deMize,
I know you are intentionally limiting input from 0 .. 99 to avoid ambiguity in the spelling of larger numbers. On the other hand, if you decided on a standard You could extend the challenge to golfing the implementation of the spelling AND the 4 is magic.

Cheers - L~R

I should say, that I this is not my puzzle. I merely copy/pasted it from the embedded link. I take no credit for this, despite how fun it is to do.

So far what we came up with is 139 chars:
@u=split'','4335043554366887798866555766';\$_=<>;chop;print"\$_ is ".(\$_=\$_<20?\$u[\$_]:\$u[\$_/10+18]+(\$_%10?\$u[\$_%10]:0)or"magic").".\n"while\$_
depending on the rules of golf '\n' could be substituted for an actual linebreak.

Demize
Re: Code Golf: Four is magic
by thundergnat (Deacon) on Jul 14, 2010 at 16:53 UTC

Update: 122 Just realized that there is no requirement to output to STDOUT, so output to STDERR instead and knock off another character.

```#234567890123456789012345678901234567890123456789012345678901234567890
+1234567890123456789012345678901234567890123456789012
@u="0335443554366887798866555766"=~/./g;\$_+=<>;warn"\$_ is ",\$_=\$_-4?\$_
+<20?\$u[\$_]||4:\$u[chop]+\$u[\$_+18]:magic,".\n"until/a/

Update: 123

```#234567890123456789012345678901234567890123456789012345678901234567890
+12345678901234567890123456789012345678901234567890123
@u='0335443554366887798866555766'=~/./g;\$_+=<>;print"\$_ is ",\$_=\$_-4?\$
+_<20?\$u[\$_]||4:\$u[chop]+\$u[\$_+18]:magic,".\n"until/a/

121 124 strokes

Update 2 Whoops. Broken for zero. Add three strokes.

```#    12345678901234567890123456789012345678901234567890123456789012345
+67890123456789012345678901234567890123456789012345678901234
perl -E'@u="0335443554366887798866555766"=~/./g;\$_+=<>;say"\$_ is ",\$_=
+\$_-4?\$_<20?\$u[\$_]||4:\$u[chop]+\$u[18+\$_]:magic,"."while/\d/'

or alternately, also 124 strokes:

```#234567890123456789012345678901234567890123456789012345678901234567890
+123456789012345678901234567890123456789012345678901234
@u='0335443554366887798866555766'=~/./g;\$_=pop;print"\$_ is ",\$_=\$_-4?\$
+_<20?\$u[\$_]||4:\$u[chop]+\$u[\$_+18]:magic,".\n"until/\D/

Updated: Argh. posted wrong (broken) version. Try again.

```#    12345678901234567890123456789012345678901234567890123456789012345
+67890123456789012345678901234567890123456789012345678901
perl -E'@u="0335443554366887798866555766"=~/./g;\$_+=<>;say"\$_ is ",\$_=
+\$_-4?\$_<20?\$u[\$_]:\$u[chop]+\$u[18+\$_]:magic,"."while/\d/'

or alternately, also 121 strokes:

```#234567890123456789012345678901234567890123456789012345678901234567890
+123456789012345678901234567890123456789012345678901
@u='0335443554366887798866555766'=~/./g;\$_=pop;print"\$_ is ",\$_=\$_-4?\$
+_<20?\$u[\$_]:\$u[chop]+\$u[\$_+18]:magic,".\n"until/\D/
Re: Code Golf: Four is magic
by deMize (Monk) on Jul 14, 2010 at 02:22 UTC
144 chars:
@u=split//,'4335443554366887798866555766';\$_=<>;chop;print"\$_ is ".(\$_=\$_-4?\$_<20?\$u[\$_]:\$u[\$_/10+18]+(\$_%10&&\$u[\$_%10]):0or magic).".\n"while\$_

Demize
125 chars:
@u='4335443554366887798866555766'=~/./g;\$_=pop;print"\$_ is ",\$_=\$_-4?\$_<20?\$u[\$_]:\$u[\$_/10+18]+\$u[\$_%10]:magic,".\n"until/\D/

Demize

That is broken for 30, 40, 50, etc.

Re: Code Golf: Four is magic
by TedPride (Priest) on Jul 15, 2010 at 06:32 UTC
```@d=split//,'4335443554366887798866555766';\$n=int<STDIN>;while(\$n!=4){p
+rint"\$n is ";\$n=\$n<20?\$d[\$n]:\$d[\$n/10+18]+(\$n%10==0?0:\$d[\$n%10]);prin
+t"\$n.\n";}print"4 is magic.\n";
125 characters, or 123 if you eliminate that last line break. Looks remarkably like some of the other submissions here, but I didn't cheat and look at anyone else's solutions first - guess there just aren't many ways to do this while keeping it short.
Re: Code Golf: Four is magic
by thundergnat (Deacon) on Jul 15, 2010 at 13:56 UTC

A version that returns the English names of the input 1-2 digit number.

Update: Hmmm. I just noticed on the stackoverflow page that they clarified that the numbers should be spelled out in both the left AND right columns. Phooey.

Another update: And now there's some further stipulation that there be a separator(space, hyphen, whatever) between spelled words and the input number must come in through STDIN, not as a passed parameter, so no pop @ARGV.

Update: 283

```@p=(Thir,Four,Fif,Six,Seven,Eigh,Nine);@n=("\x8",One,Two,Three,Four,Fi
+ve,@p[3..6],Ten,Eleven,Twelve,map\$_.teen,@p);s/u//for@m=map\$_.ty,Twen
+,@p;\$n[8].=t;sub n{\$n=shift;\$n?\$n<20?\$n[\$n]:"\$m[\$n/10-2]-\$n[\$n%10]":Z
+ero}\$p+=<>;warn\$m=n(\$p)," is ",\$_=\$p-4?n\$p=()=\$m=~/\w/g:magic,".\n"un
+til/c/
```0
Zero is Four.
Four is magic.

40
Forty is Five.
Five is Four.
Four is magic.

67
Sixty-Seven is Ten.
Ten is Three.
Three is Five.
Five is Four.
Four is magic.

83
Eighty-Three is Eleven.
Eleven is Six.
Six is Three.
Three is Five.
Five is Four.
Four is magic.

```

##### Versions that don't spell out right column. #####

Update:Sigh. Apparently I'm blind. Using duelafns suggestion, modifying the end conditional and some other minor tweaks.

256

```@p=(Thir,Four,Fif,Six,Seven,Eigh,Nine);@n=("",One,Two,Three,Four,Five,
+@p[3..6],Ten,Eleven,Twelve,map(\$_.teen,@p),map\$_.ty,Twen,@p);\$n[22]=~
+s/u//;\$n[8].=t;\$_+=<>;print\$z=\$_?\$_<20?\$n[\$_]:\$n[\$_/10+18].\$n[\$_%10]:
+Zero," is ",\$_=\$_-4?length\$z:magic,".\n"until/a/

or

```perl -E'@p=(Thir,Four,Fif,Six,Seven,Eigh,Nine);@n=("",One,Two,Three,Fo
+ur,Five,@p[3..6],Ten,Eleven,Twelve,map(\$_.teen,@p),map\$_.ty,Twen,@p);
+\$n[22]=~s/u//;\$n[8].=t;\$_+=<>;say\$z=\$_?\$_<20?\$n[\$_]:\$n[\$_/10+18].\$n[\$
+_%10]:Zero," is ",\$_=\$_-4?length\$z:magic,"."until/a/'

Previous versions

Proof of concept. Should be lots of room for improvement.

Update: whoops. Fixed for fourteen/forty. Curse you, irregular number names!

384 strokes.

```@u='0335443554366887798866555766'=~/./g;sub n{shift;@p=(qw/thir four f
+if six seven eigh nine/);@n=('',qw/one two three four five/,@p[3..6],
+qw/ten eleven twelve/);push@n,\$_.'teen'for@p;push@n,'twenty',;push@n,
+\$_.'ty'for@p;\$n[8].='t';\$n[22]=~s/u//;\$t=\$_?\$_<20?\$n[\$_]:\$n[\$_/10+18]
+.\$n[\$_%10]:'zero'}\$_=pop;print n(\$_)," is ",\$_=\$_-4?\$_<20?\$u[\$_]||4:\$
+u[chop]+\$u[18+\$_]:magic,".\n"while/\d/
```C:\test>magic.pl 0
zero is 4.
four is magic.

C:\test>magic.pl 1
one is 3.
three is 5.
five is 4.
four is magic.

C:\test>magic.pl 14
fourteen is 8.
eight is 5.
five is 4.
four is magic.

C:\test>magic.pl 15
fifteen is 7.
seven is 5.
five is 4.
four is magic.

C:\test>magic.pl 18
eighteen is 8.
eight is 5.
five is 4.
four is magic.

C:\test>magic.pl 44
fortyfour is 9.
nine is 4.
four is magic.

C:\test>magic.pl 77
seventyseven is 12.
twelve is 6.
six is 3.
three is 5.
five is 4.
four is magic.

C:\test>magic.pl 80
eighty is 6.
six is 3.
three is 5.
five is 4.
four is magic.

C:\test>magic.pl 99
ninetynine is 10.
ten is 3.
three is 5.
five is 4.
four is magic.
```

So, um, what's @u still doing in there? :)

Update: Removing @u, using do{} instead of a sub, and applying the "say" cheat gives 261 260 (until/m/) strokes

```    #123456789 123456789 123456789 123456789 123456789 123456789 12345
+6789 123456789 123456789 123456789_123456789 123456789 123456789 1234
+56789 123456789 123456789 123456789 123456789 123456789 123456789_123
+456789 123456789 123456789 123456789 123456789 123456789
for f in 0 4 12 99; do
perl -E'\$_=pop;say\$z=do{@p=(Thir,Four,Fif,Six,Seven,Eigh,Nine);@n=("",
+One,Two,Three,Four,Five,@p[3..6],Ten,Eleven,Twelve,map(\$_.teen,@p),ma
+p\$_.ty,Twen,@p);\$n[22]=~s/u//;\$n[8].=t;\$_?\$_<20?\$n[\$_]:\$n[\$_/10+18].\$
+n[\$_%10]:Zero}," is ",\$_=\$_-4?length\$z:magic,"."until/m/' \$f ; \
echo; done

Good Day,
Dean

Ummm........ Wasting about 30 strokes? Apparently I have very selective blindness...

Create A New User
Node Status?
node history
Node Type: perlmeditation [id://849126]
Approved by BrowserUk
Front-paged by Limbic~Region
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2018-04-26 21:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
My travels bear the most uncanny semblance to ...

Results (97 votes). Check out past polls.

Notices?