88

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.

(Double bonus for drawing shapes with your code)

(EDIT) Some clarifications:

  1. I do want the word to appear on both sides in all applicable cases, e.g. Nine is four. Four is magic.
  2. I don't care about capitalization, though. And I don't care how you separate the word tokens, though they should be separated: ninety-nine is okay, ninety nine is okay, ninetynine is not okay.

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.

Community
  • 1
  • 1
Platinum Azure
  • 45,269
  • 12
  • 110
  • 134
  • 1
    How high a number should we handle? < 100? < 1000? < 1000000? < 2**31? – P Daddy Jul 12 '10 at 18:24
  • 1
    Since this only has to go from 0 to 99, I suspect a quick, short solution would be to hardcode the values that 0-99 map to, and then loop until you reach 4. After that, the microtweaking begins. – Beska Jul 12 '10 at 18:26
  • @P Daddy...part 6 says only 0-99. – Beska Jul 12 '10 at 18:26
  • @P Daddy: As I said in the spec, < 100 (0-99). If you WANT to go for more, though, I guess I could make that a bonus. I figure if it went too much higher, though, any necessary storage would just become too large a part of the character count, with the actual optimizable code only representing a small portion of the actual code. – Platinum Azure Jul 12 '10 at 18:27
  • well you actually only need to hardcode 1-19, 20(twenty), 30(thirty), 40, 50, 60, 70, 80, 90. No need to hard-code any non divisible by 10 entity as it is just a compound word (ninety eight). So, I see an array of 28 items, an if statement, a mod statement, an integer division statement and a while loop /recursion (pick your flavor) – Tommy Jul 12 '10 at 18:31
  • Spoiler: Data for 0 - 99: http://pastie.org/1041288 – kennytm Jul 12 '10 at 18:31
  • http://www.easysurf.cc/cnvert18.htm - look at the source code here for numbers up to 15 digits long. – Tommy Jul 12 '10 at 18:31
  • @Tommy: Probably, but the challenge is how to do all that in as few characters as you can! This is code golf, after all. :-) – Platinum Azure Jul 12 '10 at 18:31
  • I would think hard coding each element would take up more room than a if/mod/division statement :) – Tommy Jul 12 '10 at 18:32
  • 7
    See also: http://stackoverflow.com/questions/309884/code-golf-number-to-words – mob Jul 12 '10 at 18:39
  • Anyone want to explain the close vote, and how I can make the question/challenge more clear? I know I had rather a lot of background information, but it was necessary for those who might not have known the puzzle. – Platinum Azure Jul 12 '10 at 20:32
  • Character count or octet (byte) count for the shortest solution? – Joey Jul 12 '10 at 20:45
  • @Johannes Rössel: Characters. :-) – Platinum Azure Jul 12 '10 at 20:55
  • Is it allowed to have lines like `4 is 4.` before the line saying `4 is magic.`? – Ventero Jul 12 '10 at 22:06
  • @Ventero: Nope. Should go straight to 4 is magic. – Platinum Azure Jul 12 '10 at 23:04
  • 14
    4 is only magic because it was chosen by a fair dice roll. – VirtuosiMedia Jul 12 '10 at 23:43
  • Seems a bit language specific to handle the input as well. – Matt Mitchell Jul 13 '10 at 01:03
  • @Graphain: How so? Pretty much all languages have some sort of input facility... By your argument, doesn't it seem like doing ANYTHING in a language is too language-specific? ...Like *using a language* is too language-specific? – Platinum Azure Jul 13 '10 at 16:07
  • You're clearly giving an advantage to Lisp, though ;) – Joey Jul 13 '10 at 16:36
  • @Jahannes: I didn't know it had that functionality :-( – Platinum Azure Jul 13 '10 at 18:13
  • This has to be one of my favorite Code Golf challenges ever. – JasCav Jul 13 '10 at 21:09
  • @Jason: Thanks! :-) I just wish people who are downvoting or voting to close would come forward and explain why. :-( – Platinum Azure Jul 13 '10 at 22:35
  • Ignore them. They're apparently unable to find the »ignore tags« feature. – Joey Jul 13 '10 at 23:07
  • @Platinum - well PHP would have to take $_POST vars, java has to create reader boilerplate, perl can just have it piped in etc. – Matt Mitchell Jul 14 '10 at 00:51
  • @Graphain: These are just reasons some languages are better suited to Code Golf than others. – P Daddy Jul 14 '10 at 00:58
  • @P Daddy it just seems like an evener ground is found by saying "take input, return output" in a programattic sense. Meh – Matt Mitchell Jul 14 '10 at 01:06
  • @Graphain: Eh, that's a good point I guess. I think I'm going to stick to my guns on stdin for this challenge, since the specs have been up for a while, but I'll remember that in future... This is my first code golf challenge, so I guess I have a lot to learn! Sorry about that. – Platinum Azure Jul 14 '10 at 13:53
  • "reliably proctor this puzzle yourself." What??? – Ash Jul 15 '10 at 07:22
  • @Ash: By which I mean, you can test someone else with it now that you know the transformation function. – Platinum Azure Jul 15 '10 at 13:50
  • Code golf has been discussed on meta several times. I stand by my position that it doesn't belong, but [defer to the consensus](http://meta.stackexchange.com/questions/20912/so-weekly-code-golf); I also stand by my feeling on [what makes a good one](http://meta.stackexchange.com/questions/24242/acceptable-level-of-code-golf-questions) if we must have them. This example failed to capture my interest in any way, but isn't objectionable enough for me to vote against. Most [code-golf]s survive, but I figure that having them closed is just a risk you take. – dmckee --- ex-moderator kitten Jul 15 '10 at 19:31

30 Answers30

85

Perl, about 147 char

Loosely based on Platinum Azure's solution:

               chop
              ($_.=
              <>);@
             u="433
            5443554
           366  887
          798   866
         555    766
        "=~     /\d
       /gx      ;#4
      sub       r{4
     -$_        ?$_
    <20         ?$u
   [$_          ]:(
  $'?           $u[
 $']            :0)
+$u[18+$&]:magic}print"
$_ is ",$_=r(),'.'while
                /\d
                /x;
                444
mob
  • 117,087
  • 18
  • 149
  • 283
  • 1
    @Platinum Azure the way this gets it's input is through the use of `pop`, without any arguments. Outside of a subroutine `pop` removes, and returns the last value of `@ARGV` which is the list of arguments to the Perl program. It could just as easily be replaced with `shift`, but that adds another 2 characters. See: http://p3rl.org/pop – Brad Gilbert Jul 14 '10 at 02:48
  • it looks like you need a newline character in `'.'`, which is 2 for `\n` or 1 if you're counting whitespace in the `'. '` (space being the newline literal) – vol7ron Jul 14 '10 at 03:26
  • A bit longer, but creativity goes a long way in my book. – Beska Jul 14 '10 at 13:31
  • @Platinum Azure et al: He is getting his input from stdin. That's the way to do it in Perl. (Maybe he changed it after your comment?) – Frank Jul 14 '10 at 15:04
  • @P Daddy: *groan* but +1 to your comment anyway – Platinum Azure Jul 15 '10 at 13:51
57

GolfScript - 101 96 93 92 91 90 94 86 bytes

90 → 94: Fixed output for multiples of 10.
94 → 86: Restructured code. Using base 100 to remove non-printable characters.
86 → 85: Shorter cast to string.

{n+~."+#,#6$DWOXB79Bd")base`1/10/~{~2${~1$+}%(;+~}%++=" is "\".
"1$4$4-}do;;;"magic."
Nabb
  • 3,434
  • 3
  • 22
  • 32
  • why is this so far down? it's shorter than the lisp one and doesn't use a built-in formatting function – Claudiu Jul 13 '10 at 13:41
  • 36
    I like how the code ends with `"magic."`, it pretty much sums it up. – Aistina Jul 13 '10 at 15:09
  • @Aistina: That's easy to do in this challenge, I think. :-) – Platinum Azure Jul 13 '10 at 16:14
  • 9
    @Aistina: haha, that is kinda funny. "mumbo jumbo yada yada..magic" – vol7ron Jul 14 '10 at 01:37
  • GolfScript should be considered a hack. I'm glad it beats Lisp, but in the real rules of Golf, there is no GolfScript allowed :) – vol7ron Jul 14 '10 at 02:34
  • That string looks awfully familiar. ;) What's the "d" at the end for? – P Daddy Jul 14 '10 at 03:31
  • 1
    @P Daddy The `d` is extracted by the `)` as `100` and is used as the radix for the base conversion. – Nabb Jul 14 '10 at 04:26
  • I think this is the clear winner at this point. I'm accepting this now in case the question ends up being closed... Well done, Nabb! (and KennyTM too) – Platinum Azure Jul 14 '10 at 14:53
  • I love how this doesn't actually contain the words "one, two, three, four" etc like the other answers do. – Dolph Jul 16 '10 at 00:57
  • @Claudiu: because it's a language that was specifically designed for the game of golf, hence the name **Golf** Script. It's like going fishing with dynamite. You'll get the job done, but you miss the art in doing it. Generally, traditional languages are the only ones allowed. – vol7ron Jul 22 '10 at 03:02
  • @vol7ron: golfscript is turing-complete, though. it can do anythig a traditional language can do (and one of the projects i want to do soon is to write a core javascript to golfscript compiler). it's pretty much J without so many built-ins, but people seem to allow J in these competitions. – Claudiu Jul 22 '10 at 17:26
30

Common Lisp 157 Chars

New more conforming version, now reading form standard input and ignoring spaces and hyphens:

(labels((g (x)(if(= x 4)(princ"4 is magic.")(let((n(length(remove-if(lambda(x)(find x" -"))(format nil"~r"x)))))(format t"~a is ~a.~%"x n)(g n)))))(g(read)))

In human-readable form:

 (labels ((g (x)
           (if (= x 4)
            (princ "4 is magic.")
            (let ((n (length (remove-if (lambda(x) (find x " -"))
                                        (format nil "~r" x)))))
               (format t"~a is ~a.~%" x n)
               (g n)))))
    (g (read)))

And some test runs:

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

>23152436
23152436 is 64.
64 is 9.
9 is 4.
4 is magic.

And the bonus version, at 165 chars:

 (labels((g(x)(if(= x 4)(princ"four is magic.")(let*((f(format nil"~r"x))(n(length(remove-if(lambda(x)(find x" -"))f))))(format t"~a is ~r.~%"f n)(g n)))))(g(read)))

Giving

>24
twenty-four is ten.
ten is three.
three is five.
five is four.
four is magic.

>234235
two hundred thirty-four thousand two hundred thirty-five is forty-eight.
forty-eight is ten.
ten is three.
three is five.
five is four.
four is magic.
  • Okay, now show us how the Bonus is done. Shouldn't be too much different, right? BTW, does Common Lisp say [a billion is 10^9 (short scale) or 10^12 (long scale)?](http://en.wikipedia.org/wiki/Long_and_short_scales) – Mike DeSimone Jul 12 '10 at 23:55
  • 5
    I thought "twenty-four" only has 10 letters? – kennytm Jul 13 '10 at 05:56
  • 1
    The numbers after "is" should be text, too. – Mike DeSimone Jul 13 '10 at 11:36
  • @KennyTM, depends on if "long-term" is a 9 or 8 letter word, I'm not a native speaker, but in Norway we count hyphens and spaces as letters when talking about word-length. –  Jul 13 '10 at 11:58
  • @Mike DeSimone Feel free to edit, I like this version because it gives counts as well, making it easier to verify. –  Jul 13 '10 at 11:59
  • 1
    I think this violates rule #2 - input must be read from standard input. – Ferruccio Jul 13 '10 at 13:00
  • 1
    @johanbev: The spec says "ignore a hyphen or spaces or "and"". – kennytm Jul 13 '10 at 13:14
  • 5
    why is this so high up? other ones don't use a built-in format function and they are less characters – Claudiu Jul 13 '10 at 13:41
  • 3
    @Claudiu Because Common Lisp is awesome. – Mornedhel Jul 13 '10 at 14:54
  • I know the spec is a little arbitrary, but I did specifically state not to count hyphens or spaces or "and", strictly letters that form the component numerical words. :-) – Platinum Azure Jul 13 '10 at 16:05
  • @johanbev: As Mike DeSimone says, the numbers after "is" should be text too. Sorry for not making that clear (I see now that I didn't actually put that explicitly in the spec). I'll fix that shortly. – Platinum Azure Jul 13 '10 at 16:06
  • As KennyTM said, this is an invalid answer. Example from question: `42 is 8` ignoring spaces and hyphens it's 8, your way has it listed as 9, which means you are not ignoring. – vol7ron Jul 13 '10 at 16:07
  • 3
    It doesn't matter how many strokes you take if you don't get the ball in the hole. People seem to forget that when they upvote incorrect solutions. – Mark Peters Jul 13 '10 at 18:06
  • The bonus one should properly capitalize the first word in its sentences!!! English FTW! – Thomas Eding Jul 18 '10 at 18:27
21

Python 2.x, 144 150 154 166 chars

This separates the number into tens and ones and sum them up. The undesirable property of the pseudo-ternary operator a and b or c that c is returned if b is 0 is being abused here.

n=input()
x=0x4d2d0f47815890bd2
while n-4:p=n<20and x/10**n%10or 44378/4**(n/10-2)%4+x/10**(n%10)%10+4;print n,"is %d."%p;n=p
print"4 is magic."

The previous naive version (150 chars). Just encode all lengths as an integer.

n=input()
while n-4:p=3+int('1yrof7i9b1lsi207bozyzg2m7sclycst0zsczde5oks6zt8pedmnup5omwfx56b29',36)/10**n%10;print n,"is %d."%p;n=p
print"4 is magic."
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Sorry, I specifically wanted full stops just because of things like this. :-) Good entry though! (EDIT: I don't know Python, but could you `n,"is",p,"."`? I think you still save some chararacters if I'm counting right) – Platinum Azure Jul 12 '10 at 19:26
  • 2
    @Plat: That would cause an extra space before the `.`. – kennytm Jul 12 '10 at 19:29
  • @KennyTM: Oh, duh, I should have noticed that even from the snippet. Oops! Well, anyway, as I said, some of the specifications were specifically designed to complicate things a bit. :-) – Platinum Azure Jul 12 '10 at 19:30
  • Can we shorten this by using a higher base than 36? – MikeD Jul 12 '10 at 20:45
  • @MikeD: Nope. From the Python docs: "The *base* parameter gives the base for the conversion (which is 10 by default) and may be any integer in the range [2, 36], or zero." Now you *might* be able to use a function other than `int()`, say something out of the `struct` or `base64` modules... – Mike DeSimone Jul 12 '10 at 21:02
20

C - with number words

445 431 427 421 399 386 371 359* 356 354 348 347 characters

That's it. I don't think I can make this any shorter.

All newlines are for readability and can be removed:

i;P(x){char*p=",one,two,three,four,five,six,sM,eight,nine,tL,elM,twelve,NP,4P,
fifP,6P,7P,8O,9P,twLQ,NQ,forQ,fifQ,6Q,7Q,8y,9Q,en,evL,thir,eL,tO,ty, is ,.\n,
4RmagicS,zero,";while(x--)if(*++p-44&&!x++)*p>95|*p<48?putchar(*p),++i:P(*p-48);
}main(c){for(scanf("%d",&c);c+(i=-4);P(34),P(c=i),P(35))P(c?c>19?P(c/10+18),
(c%=10)&&putchar(45):0,c:37);P(36);}

Below, it is somewhat unminified, but still pretty hard to read. See below for a more readable version.

i;
P(x){
    char*p=",one,two,three,four,five,six,sM,eight,nine,tL,elM,twelve,NP,4P,fifP,6P,7P,8O,9P,twLQ,NQ,forQ,fifQ,6Q,7Q,8y,9Q,en,evL,thir,eL,tO,ty, is ,.\n,4RmagicS,zero,";
    while(x--)
        if(*++p-44&&!x++)
            *p>95|*p<48?putchar(*p),++i:P(*p-48);
}
main(c){
    for(scanf("%d",&c);c+(i=-4);P(34),P(c=i),P(35))
        P(c?
            c>19?
                P(c/10+18),
                (c%=10)&&
                    putchar(45)
            :0,
            c
        :37);
    P(36);
}

Expanded and commented:

int count; /* type int is assumed in the minified version */

void print(int index){ /* the minified version assumes a return type of int, but it's ignored */
    /* see explanation of this string after code */
    char *word =
        /* 1 - 9 */
        ",one,two,three,four,five,six,sM,eight,nine,"
        /* 10 - 19 */
        "tL,elM,twelve,NP,4P,fifP,6P,7P,8O,9P,"
        /* 20 - 90, by tens */
        "twLQ,NQ,forQ,fifQ,6Q,7Q,8y,9Q,"
        /* lookup table */
        "en,evL,thir,eL,tO,ty, is ,.\n,4RmagicS,zero,";

    while(index >= 0){
        if(*word == ',')
            index--;
        else if(index == 0) /* we found the right word */
            if(*word >= '0' && *word < 'a') /* a compression marker */
                print(*word - '0'/*convert to a number*/);
            else{
                putchar(*word); /* write the letter to the output */
                ++count;
            }
        ++word;
    }
}
int main(int argc, char **argv){ /* see note about this after code */
    scanf("%d", &argc); /* parse user input to an integer */

    while(argc != 4){
        count = 0;
        if(argc == 0)
            print(37/*index of "zero"*/);
        else{
            if(argc > 19){
                print(argc / 10/*high digit*/ + 20/*offset of "twenty"*/ - 2/*20 / 10*/);
                argc %= 10; /* get low digit */

                if(argc != 0) /* we need a hyphen before the low digit */
                    putchar('-');
            }
            print(argc/* if 0, then nothing is printed or counted */);
        }
        argc = count;
        print(34/*" is "*/);
        print(argc); /* print count as word */
        print(35/*".\n"*/);
    }
    print(36/*"four is magic.\n"*/);
}

About the encoded string near the beginning

The names of the numbers are compressed using a very simple scheme. Frequently used substrings are replaced with one-character indices into the name array. A "lookup table" of extra name entries is added to the end for substrings not used in their entirety in the first set. Lookups are recursive: entries can refer to other entries.

For instance, the compressed name for 11 is elM. The print() function outputs the characters e and l (lower-case 'L', not number '1') verbatim, but then it finds the M, so it calls itself with the index of the 29th entry (ASCII 'M' - ASCII '0') into the lookup table. This string is evL, so it outputs e and v, then calls itself again with the index of the 28th entry in the lookup table, which is en, and is output verbatim. This is useful because en is also used in eL for een (used after eight in eighteen), which is used in tO for teen (used for every other -teen name).

This scheme results in a fairly significant compression of the number names, while requiring only a small amount of code to decompress.

The commas at the beginning and end of the string account for the simplistic way that substrings are found within this string. Adding two characters here saves more characters later.

About the abuse of main()

argv is ignored (and therefore not declared in the compressed version), argc's value is ignored, but the storage is reused to hold the current number. This just saves me from having to declare an extra variable.

About the lack of #include

Some will complain that omitting #include <stdio.h> is cheating. It is not at all. The given is a completely legal C program that will compile correctly on any C compiler I know of (albeit with warnings). Lacking protoypes for the stdio functions, the compiler will assume that they are cdecl functions returning int, and will trust that you know what arguments to pass. The return values are ignored in this program, anyway, and they are all cdecl ("C" calling convention) functions, and we do indeed know what arguments to pass.

Output

Output is as expected:

0
zero is four.
four is magic.
1
one is three.
three is five.
five is four.
four is magic.
4
four is magic.
20
twenty is six.
six is three.
three is five.
five is four.
four is magic.
21
twenty-one is nine.
nine is four.
four is magic.

* The previous version missed the mark on two parts of the spec: it didn't handle zero, and it took input on the command line instead of stdin. Handling zeros added characters, but using stdin instead of command line args, as well as a couple of other optimzations saved the same number of characters, resulting in a wash.

The requirements have been changed to make clear that the number word should be printed on both sides of " is ". This new version meets that requirement, and implements a couple more optimizations to (more than) account for the extra size necessary.

P Daddy
  • 28,912
  • 9
  • 68
  • 92
  • This is easily my favorite of the word answers... Bravo, well done. +1 for you, and if I could give two checkmarks I would. – Platinum Azure Jul 14 '10 at 20:49
  • 5
    That's fun to read, I think I will use these numbers from now on in daily life. Six, sem, eight, nine, tel, elem, twelve, enpee, fourpee, fifpee, sixpee, sevenpee, eightoh, ninepee, twelkyu... =) – deceze Jul 15 '10 at 03:45
10

J, 107 112 characters

'4 is magic.',~}:('.',~":@{.,' is ',":@{:)"1]2&{.\.
(]{&(#.100 4$,#:3 u:ucp'䌵䐵吶梇禈榛ꪛ멩鮪鮺墊馊꥘誙誩墊馊ꥺ겻곋榛ꪛ멩鮪鮺'))^:a:

(Newline for readability only)

Usage and output:

    '4 is magic.',~}:('.',~":@{.,' is ',":@{:)"1]2&{.\.(]{&(#.100 4$,#:3 u:ucp'䌵䐵吶梇禈榛ꪛ멩鮪鮺墊馊꥘誙誩墊馊ꥺ겻곋榛ꪛ멩鮪鮺'))^:a:12
12 is 6.    
6 is 3.     
3 is 5.     
5 is 4.     
4 is magic. 
David
  • 7,011
  • 1
  • 42
  • 38
10

T-SQL, 413 451 499 chars

CREATE FUNCTION d(@N int) RETURNS int AS BEGIN
Declare @l char(50), @s char(50)
Select @l='0066555766',@s='03354435543668877987'
if @N<20 return 0+substring(@s,@N+1,1) return 0+substring(@l,(@N/10)+1,1) + 0+(substring(@s,@N%10+1,1))END
GO
CREATE proc M(@x int) as BEGIN
WITH r(p,n)AS(SELECT p=@x,n=dbo.d(@x) UNION ALL SELECT p=n,n=dbo.d(n) FROM r where n<>4)Select p,'is',n,'.' from r print '4 is magic.'END

(Not that I'm seriously suggesting you'd do this... really I just wanted to write a CTE)

To use:

M 95

Returns

p                n
----------- ---- -----------
95          is   10.
10          is   3.
3           is   5.
5           is   4.
4 is magic.
Community
  • 1
  • 1
Leon Bambrick
  • 26,009
  • 9
  • 51
  • 75
  • Can't you just print the individual results instead of returning a table? That would make the output look nicer. – Joey Jul 13 '10 at 13:30
  • 1
    I don't think it handles zero properly. How about something like this: `CREATE FUNCTION d(@ int) RETURNS int AS BEGIN Declare @l char(9),@s char(50) Select @l='066555766',@s='03354435543668877987' if @=0 return 4 if @<20 return 0+substring(@s,@+1,1)return 0+substring(@l,@/10,1)+substring(@s,@%10+1,1)END` – Gabe Jul 15 '10 at 08:35
9

Java (with boilerplate), 308 290 286 282 280 characters

class A{public static void main(String[]a){int i=4,j=0;for(;;)System.out.printf("%d is %s.%n",i=i==4?new java.util.Scanner(System.in).nextInt():j,i!=4?j="43354435543668877988699;::9;;:699;::9;;:588:998::9588:998::9588:998::97::<;;:<<;699;::9;;:699;::9;;:".charAt(i)-48:"magic");}}

I'm sure Groovy would get rid of much of that.

Explanation and formatting (all comments, newlines and leading/trailing whitespace removed in count):

Reasonably straight forward, but

//boilerplate
class A{
   public static void main(String[]a){
      //i is current/left number, j right/next number.  i=4 signals to start
      //by reading input
      int i=4,j=0;
      for(;;)
         //print in the form "<left> is <right>."
         System.out.printf(
            "%d is %s.%n",
            i=i==4?
               //<left>: if i is 4 <left> will be a new starting number
               new java.util.Scanner(System.in).nextInt():
               //otherwise it's the next val
               j,
            i!=4?
               //use string to map number to its length (:;< come after 9 in ASCII)
               //48 is value of '0'.  store in j for next iteration
               j="43354435543668877988699;::9;;:699;::9;;:588:998::9588:998::9588:998::97::<;;:<<;699;::9;;:699;::9;;:".charAt(i)-48:
               //i==4 is special case for right; print "magic"
               "magic");
   }
}

Edit: No longer use hex, this is less keystrokes

Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • 1
    249 without imports, class def or main def. – Mark Peters Jul 12 '10 at 19:29
  • 1
    That's fiendish. I like the base 16. (+1) – Platinum Azure Jul 12 '10 at 20:01
  • You could save one space by using `String[]a` instead of `String[] a`. – BalusC Jul 12 '10 at 20:19
  • Thanks @Balus, also eliminated a bunch by doing simple arithmetic on the character instead of using hex parsing. – Mark Peters Jul 12 '10 at 20:34
  • @Mark Peters: Even nastier. I feel so vanilla compared to that. – Platinum Azure Jul 12 '10 at 20:43
  • Alright it's as nasty as it gets now, and I've stopped trying to update the explanation with it. Now all of the logic happens as you evaluate the parameters to println :-) and I've removed the brackets that were making it the least bit readable, all to save 8 keystrokes. – Mark Peters Jul 12 '10 at 20:45
  • "I'm sure Groovy would get rid of much of that." ... Please demonstrate for those of us who have no idea what "Groovy" is or does. – Mike DeSimone Jul 13 '10 at 02:46
  • @Mike: Groovy is a multi-paradigm "superset" of Java that compiles to JVM bytecode. Almost every Java program is a valid Groovy program, but Groovy also does away with a lot of boilerplate. Notably, I believe you can do without the explicit class definition and `main` definition and just write code, like you can in perl, etc. I've never used it and don't have the dev environment though, so I'm not the one to post an example. Just imagine everything before `int...` being removed. – Mark Peters Jul 13 '10 at 04:05
9

Windows PowerShell: 152 153 184 bytes

based on the previous solution, with more influence from other solutions

$o="03354435543668877988"
for($input|sv b;($a=$b)-4){if(!($b=$o[$a])){$b=$o[$a%10]-48+"66555766"[($a-$a%10)/10-2]}$b-=48-4*!$a
"$a is $b."}'4 is magic.'
Joey
  • 344,408
  • 85
  • 689
  • 683
  • Fixed to support multiples of 10 ("ninety" rather than "ninetyzero"). – Gabe Jul 14 '10 at 05:44
  • Hey @Gabe :), thanks; haven't had much time for golfing lately. Still, the quotes around `$input` have to remain since you can't cast an enumerator directly to `int`; it works when going through `string` first :-) – Joey Jul 14 '10 at 08:50
8

C, 158 characters

main(n,c){char*d="03354435543668877988";for(scanf("%d",&n);n-4;n=c)printf("%d is %d.\n",n,c=n?n<19?d[n]-48:d[n%10]-"_,**+++)**"[n/10]:4);puts("4 is magic.");}

(originally based on Vlad's Python code, borrowed a trick from Tom Sirgedas' C++ solution to squeeze out a few more characters)

expanded version:

main(n, c) {
    char *d = "03354435543668877988";
    for (scanf("%d",&n); n-4; n = c)
        printf("%d is %d.\n", n, c = n ? n<19 ? d[n]-48 : d[n%10] - "_,**+++)**"[n/10]  : 4);
    puts("4 is magic.");
}
Ferruccio
  • 98,941
  • 38
  • 226
  • 299
  • Doesn't seem to work for me: ./magic 10 10 is -27. Segmentation fault – Casey Jul 13 '10 at 01:22
  • @Casey - the scanf() call was a little sketchy. It was reading an int into a char. I was getting away with it on OSX and on Windows it worked but crashed on exit. So, I made n & c ints again. I realized I could drop the int keyword by making them parameters using K&R notation. The result is safer and one character shorter. – Ferruccio Jul 13 '10 at 02:19
  • You can save 3 characters by replacing "_466555766"[n/10]+d[n%10]-96 with d[n%10]-"_,**+++)**"[n/10] – Tom Sirgedas Jul 15 '10 at 19:31
6

Perl: 148 characters

(Perl: 233 181 212 206 200 199 198 185 179 149 148 characters)

  • Moved exceptions hash into unit array. This resulted in my being able to cut a lot of characters :-)
  • mobrule pointed out a nasty bug. Quick fix adds 31 characters, ouch!
  • Refactored for zero special case, mild golfing done as well.
  • Direct list access for single use rather than storing to array? Hell yes!
  • SO MUCH REFACTORING for just ONE bloody character. This, truly, is the life of a golfer. :-(
  • Oops, easy whitespace fix. 198 now.
  • Refactored some redundant code.
  • Last return keyword in r is unnecessary, shaved some more off.
  • Massive refactoring per comments; unfortunately I could only get it to 149 because I had to fix a bug that was present in both my earlier code and the commenters' versions.
  • Trying bareword "magic".

Let's get this ball rolling with a modest attempt in Perl.

@u=split'','4335443554366887798866555766';$_=<>;chop;print"$_ is ".($_=$_==4?0:$_<20?$u[$_]:($u[$_/10+18]+($_%10&&$u[$_%10]))or magic).".
"while$_

Tricks:

Too many!

Platinum Azure
  • 45,269
  • 12
  • 110
  • 134
  • ACK! How I never tested that I'll never know. – Platinum Azure Jul 12 '10 at 20:20
  • Do you have some dead code in there? I'm not seeing how the special case for zero is necessary when $u[0] is 4. I have a seemingly-working version of your code @166 chars, and I think it has room to get a bit shorter than that. – hobbs Jul 12 '10 at 21:41
  • @hobbs: Good point, I'll look again. The story is that I got halfway through a couple of revisions and suddenly things broke (at about the point where I chose to have 4 --> 0). I think you're right at this point, though :-) – Platinum Azure Jul 12 '10 at 23:06
  • I don't consider myself a great Perl programmer, but you can reduce some characters: `@u=split$x,'43350435543668877988';` your commas use an unnecessary 19 characters, splitting on an `undef` splits at every character, i use `$x` as an undefined variable to take place of ` undef` -- total savings: 11 characters. Also, remove the `m` in `chomp` and you get another character shaved off your score. – vol7ron Jul 13 '10 at 00:27
  • Doing better, but you can still save more by losing `sub r` entirely -- you only use it once and you *can* replace it all by a single nested ternary without even parens. My version is 144 chars right now: http://gist.github.com/473289 – hobbs Jul 13 '10 at 00:37
  • i decided not to cheat and just made it a one-liner substituting the newline for `\n`. With the `\n` it's 145, otherwise it would be 144/143 chars, depending if you count the linefeed as 1 or 0 characters. – vol7ron Jul 13 '10 at 01:04
  • By combining the lists and modifying the math: `@u=split'','4335043554366887798866555766';$_=<>;chop;print"$_ is ".($_=$_<20?$u[$_]:$u[$_/10+18]+($_%10?$u[$_%10]:0)or"magic").".\n"while$_` @ 139, or if whitespace is counted, you could turn that `\n` to a return and be at 137 – vol7ron Jul 13 '10 at 01:27
  • You can also save 2 more chars : ($_%10&&$u[$_%10]) instead of ($_%10?$u[$_%10]:0) and explicit newline (1char) instead of \n (2chars) – Toto Jul 13 '10 at 09:46
  • good call on the and operator (`&&`)! the newline has already been mentioned - i consider golf to be one-liners, which explicit newlines violate – vol7ron Jul 13 '10 at 12:49
  • @vol7ron: How does your code deal with 0, 20, 24, 30, etc.? I'd have problems with getting those consistent in the past and I haven't got time to test. – Platinum Azure Jul 13 '10 at 12:57
  • Now I have it working... I'll see if I can find a way to get rid of the explicit case or at least shorten the conditional. – Platinum Azure Jul 13 '10 at 14:25
  • if you're using one of the later versions of Perl, you can use `say` instead of `print`. Not only is it shorter, it also appends a `\n` automatically to the end. – vol7ron Jul 13 '10 at 16:18
  • also change that `&&` down to `&` – vol7ron Jul 13 '10 at 16:37
  • if still using `print` and `\n` it's **145**. If using `say` it's **141** characters – vol7ron Jul 13 '10 at 16:40
  • @vol7ron: If I do s/&&/& I might need to add a space, in case Perl sees it as a sub reference or something. But I'll look into it. – Platinum Azure Jul 13 '10 at 17:02
  • I believe you can shave off one character by getting rid of the quotes around `magic` (treating it as a bareword). The leading " will be replaced by a single space. – toolic Jul 13 '10 at 17:23
  • **@Platinum Azure:** I'm not entirely sure. `&` acts as a bitwise operator (http://perldoc.perl.org/perlop.html#Bitwise-And). In this case, it has the same result, but I've never needed to use it in other cases, so I couldn't tell you too much of what's going on. -- toolic is also right, you can remove the trailing quote and change the beginning to a space – vol7ron Jul 13 '10 at 21:30
  • mobrule brought up a good point `$_==4?0:...` to `$_-4?...:0` to save another character – vol7ron Jul 13 '10 at 22:31
  • I was wrong about the bitwise-and, it was failing for values in the 90's and probably elsewhere. – vol7ron Jul 14 '10 at 02:29
  • @vol7ron: My worry is that I might need to think about precedence and insert parentheses; that was the reason for my original decision. I'd only add one more character at worst, though, and maybe get rid of one if I don't need it... If someone else wants to try it and then edit my answer if it does work for them, please feel free. – Platinum Azure Jul 14 '10 at 13:57
  • **@Platinum Azure:** I've already started my own. Check for my Perl answer currently at 121/125 (depending on version) :) – vol7ron Jul 14 '10 at 15:04
6

C#: 210 Characters.

Squished:

using C=System.Console;class B{static void Main(){int
x=0,y=int.Parse(C.ReadLine());while(x!=4)C.Write((x=y)+" is {0}.\n",x==4?"magic":""+(y=x==0?4:"03354435543668877988"[x<20?x:x%10]+"0066555766"[x/10]-96));}}

Expanded:

using C=System.Console;
class B
{
    static void Main()
    {
        int x=0,y=int.Parse(C.ReadLine());
        while(x!=4)
            C.Write((x=y)+" is {0}.\n",
                x==4?
                     "magic":
                     ""+(y= x==0?
                                4:
                                "03354435543668877988"[x<20?x:x%10]+
                                "0066555766"[x/10]-96)
                   );
    }
}

Tricks this approach uses:

  • Create a lookup table for number name lengths based on digits that appear in the number.
  • Use character array lookup on a string, and char arithmetic instead of a numeric array.
  • Use class name aliasing to short Console. to C.
  • Use the conditional (ternary) operator (?:) instead of if/else.
  • Use the \n with Write escape code instead of WriteLine
  • Use the fact that C# has a defined order of evaluation to allow assignments inside the Write function call
  • Use the assignment expressions to eliminate extra statements, and thus extra braces
Gabe
  • 84,912
  • 12
  • 139
  • 238
LBushkin
  • 129,300
  • 32
  • 216
  • 265
6

Python, 129 133 137 148 chars

As a warm-up, here is my first version (improves couple of chars over previous best Python).

PS. After a few redactions now it is about twenty char's shorter:

n=input()
while n-4:p=(922148248>>n/10*3&7)+(632179416>>n%10*3&7)+(737280>>n&1)+4*(n<1);print n,'is %d.'%p;n=p
print'4 is magic.'
Nas Banov
  • 28,347
  • 6
  • 48
  • 67
5

JavaScript 1.8 (SpiderMonkey) - 153 Chars

l='4335443554366887798866555766'.split('')
for(b=readline();(a=+b)-4;print(a,'is '+b+'.'))b=a<20?l[a]:+l[18+a/10|0]+(a%10&&+l[a%10])
print('4 is magic.')

Usage: echo 42 | js golf.js

Output:

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

With bonus - 364 chars

l='zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty thirty fourty fifty sixty seventy eighty ninety'.split(' ')
z=function(a)a<20?l[a]:l[18+a/10|0]+(a%10?' '+l[a%10]:'')
for(b=+readline();(a=b)-4;print(z(a),'is '+z(b)+'.'))b=z(a).replace(' ','').length
print('four is magic.')

Output:

ninety nine is ten.
ten is three.
three is five.
five is four.
four is magic.
Community
  • 1
  • 1
gnarf
  • 105,192
  • 25
  • 127
  • 161
4

C++ Stdio version, minified: 196 characters

#include <cstdio>
#define P;printf(
char*o="43354435543668877988";main(int p){scanf("%d",&p)P"%d",p);while(p!=4){p=p<20?o[p]-48:"0366555966"[p/10]-96+o[p%10]P" is %d.\n%d",p,p);}P" is magic.\n");}

C++ Iostreams version, minified: 195 characters

#include <iostream>
#define O;std::cout<<
char*o="43354435543668877988";main(int p){std::cin>>p;O p;while(p!=4){p=p<20?o[p]-48:"0366555966"[p/10]-96+o[p%10]O" is "<<p<<".\n"<<p;}O" is magic.\n";}

Original, un-minified: 344 characters

#include <cstdio>

int ones[] = { 4, 3, 3, 5, 4, 4, 3, 5, 5, 4, 3, 6, 6, 8, 8, 7, 7, 9, 8, 8 };
int tens[] = { 0, 3, 6, 6, 5, 5, 5, 9, 6, 6 };

int n(int n) {
    return n<20 ? ones[n] : tens[n/10] + ones[n%10];
}

int main(int p) {
    scanf("%d", &p);
    while(p!=4) {
        int q = n(p);
        printf("%i is %i\n", p, q);
        p = q;
    }
    printf("%i is magic\n", p);
}
Karl von Moor
  • 8,484
  • 4
  • 40
  • 52
  • Fixed. It made it a bit shorter, too. – Mike DeSimone Jul 12 '10 at 20:30
  • Nicely done. (I laughed a lot at the 20-character std conundrum!) – Platinum Azure Jul 12 '10 at 20:43
  • Yeah, that was a real headbanger, until it dawned on me that `#define` would be even shorter since it could replace several tokens. – Mike DeSimone Jul 12 '10 at 20:58
  • `printf("is magic".\n)` => `puts`. `printf("%d",p)` => `puts(atoi(p))`. Not only shorter but faster too. – Ben Voigt Jul 13 '10 at 01:35
  • I shied away from those because I remember (from way back) that the interaction between `printf` and other output functions was not guaranteed. Specifically, some functions can go around the buffer that `printf` uses, resulting in out-of-order output. Has this been fixed? – Mike DeSimone Jul 13 '10 at 02:41
  • Oh, and I don't think `puts(atoi(p))` does what you think it does. Hint: there's no `itoa()`. – Mike DeSimone Jul 13 '10 at 02:42
  • 2
    @Mike DeSimone: I think `while(p!=4)` could be shortened to `while(p-4)`. One whole character, I know, but still. :-) – Platinum Azure Jul 13 '10 at 17:14
  • That's not valid C++. A standards compliant compiler should not compile it -- the names from the standard library need to have namespaces, or you need to `#include ` instead of ``. – Billy ONeal Jul 13 '10 at 21:09
  • @Billy a compiler that compiles code that includes and uses the global namespace versions _is_ conforming (or rather, compiling this code is not enough to disqualify the compiler), it's just not guaranteed. `cstdio` merely must provide definitions in the std namespace, it is also allowed to provide definitions in the global namespace. Likewise stdio.h must provide definitions in the global namespace, but it is also allowed to provide defintions in the std namespace. The problem is with the code, not the compiler. – Logan Capaldo Jul 14 '10 at 00:30
4

Haskell, 224 270 characters

o="43354435543668877988"
x!i=read[x!!i]
n x|x<20=o!x|0<1="0066555766"!div x 10+o!mod x 10
f x=zipWith(\a b->a++" is "++b++".")l(tail l)where l=map show(takeWhile(/=4)$iterate n x)++["4","magic"]
main=readLn>>=mapM putStrLn.f

And little more readable -

ones = [4,3,3,5,4,4,3,5,5,4,3,6,6,8,8,7,7,9,8,8]
tens = [0,0,6,6,5,5,5,7,6,6]

n x = if x < 20 then ones !! x else (tens !! div x 10) + (ones !! mod x 10)

f x = zipWith (\a b -> a ++ " is " ++ b ++ ".") l (tail l)
    where l = map show (takeWhile (/=4) (iterate n x)) ++ ["4", "magic"]
    
main = readLn >>= mapM putStrLn . f
Community
  • 1
  • 1
Martin Jonáš
  • 2,309
  • 15
  • 12
3

C# 314 286 283 274 289 273 252 chars.

Squished:

252 

Normal:

using C = System.Console;
class P
{
    static void Main()
    {
        var x = "4335443554366877798866555766";
        int m, o, v = int.Parse(C.ReadLine());
        do {
            C.Write("{0} is {1}.\n", o = v, v == 4 ? (object)"magic" : v = v < 20 ? x[v] - 48 : x[17 + v / 10] - 96 + ((m = v % 10) > 0 ? x[m] : 48));
        } while (o != 4);
        C.ReadLine();
    }
}

Edit Dykam: Did quite some carefull insertions and changes:

  • Changed the l.ToString() into a cast to object of the string "magic".
  • Created a temporary variable o, so I could move the break outside the for loop, that is, resulting in a do-while.
  • Inlined the o assignment, aswell the v assignment, continueing in inserting the calculation of l in the function arguments altogether, removing the need for l. Also inlined the assignment of m.
  • Removed a space in int[] x, int[]x is legit too.
  • Tried to transform the array into a string transformation, but the using System.Linq was too much to make this an improvement.

Edit 2 Dykam Changed the int array to a char array/string, added proper arithmics to correct this.

Dykam
  • 10,190
  • 4
  • 27
  • 32
mdm20
  • 4,475
  • 2
  • 22
  • 24
3

Delphi: 329 characters

Single Line Version:

program P;{$APPTYPE CONSOLE}uses SysUtils;const S=65;A='EDDFEEDFFEDGGIIHHJII';B='DGGFFFJGG';function Z(X:Byte):Byte;begin if X<20 then Z:=Ord(A[X+1])-S else Z:=(Ord(B[X DIV 10])-S)+Z(X MOD 10)end;var X,Y:Byte;begin Write('> ');ReadLn(X);repeat Y:=Z(X);WriteLn(Format('%d is %d.',[X,Y]));X:=Y;until X=4;WriteLn('4 is magic.');end.

Formated:

program P;

{$APPTYPE CONSOLE}

uses
  SysUtils;

const
  S = 65;
  A = 'EDDFEEDFFEDGGIIHHJII';
  B = 'DGGFFFJGG';

function Z(X:Byte):Byte;
begin
  if X<20
  then Z := Ord(A[X+1])-S
  else Z := (Ord(B[X DIV 10])-S) + Z(X MOD 10);
end;

var
  X,Y: Byte;

begin
  Write('> ');
  ReadLn(X);

  repeat
    Y:=Z(X);
    WriteLn(Format('%d is %d.' , [X,Y]));
    X:=Y;
  until X=4;

  WriteLn('4 is magic.');
end.

Probably room for some more squeezing... :-P

Jørn E. Angeltveit
  • 3,029
  • 3
  • 22
  • 53
3

C - without number words

180 175* 172 167 characters

All newlines are for readability and can be removed:

i;V(x){return"\3#,#6$:WOXB79B"[x/2]/(x%2?1:10)%10;}main(c){for(scanf("%d",&c);
c-4;)i=c,printf("%d is %d.\n",i,c=c?c>19?V(c/10+19)+V(c%10):V(c):4);puts(
"4 is magic.");}

Slightly unminified:

i;
V(x){return"\3#,#6$:WOXB79B"[x/2]/(x%2?1:10)%10;}
main(c){
    for(scanf("%d",&c);c-4;)
        i=c,
        printf("%d is %d.\n",i,c=c?c>19?V(c/10+19)+V(c%10):V(c):4);
    puts("4 is magic.");
}

* The previous version missed the mark on two parts of the spec: it didn't handle zero, and it took input on the command line instead of stdin. Handling zero added characters, but using stdin instead of command line args saved even more, resulting in a net savings.

P Daddy
  • 28,912
  • 9
  • 68
  • 92
3

Lua, 176 Characters

o={[0]=4,3,3,5,4,4,3,5,5,4,3,6,6,8,8,7,7,9,8,8}t={3,6,6,5,5,5,7,6,6}n=0+io.read()while n~=4 do a=o[n]or o[n%10]+t[(n-n%10)/10]print(n.." is "..a..".")n=a end print"4 is magic."

or

  o={[0]=4,3,3,5,4,4
  ,3,5,5,4,3,6,6,8,8
  ,7,7,9,8,8}t={3,6,
   6,5,5,5,7,6,6}n=
   0+io.read()while
   n ~= 4 do a= o[n
   ]or o[n%10]+t[(n
   -n%10)/10]print(
n.." is "..a.."." )n=a
end print"4 is magic."
Community
  • 1
  • 1
gwell
  • 2,695
  • 20
  • 20
2

perl, 123 122 characters

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

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

And, a version that returns spelled out numbers:

279 278 276 280 characters

@p=(Thir,Four,Fif,Six,Seven,Eigh,Nine);@n=("",One,Two,Three,Four,Five,@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]":Zero}$p+=<>;warnt$m=n($p)," is ",$_=$p-4?n$p=()=$m=~/\w/g:magic,".\n"until/c/

While that meets the spec, it is not 100% well formatted. It returns an extra space after numbers ending in zero. The spec does say:

"I don't care how you separate the word tokens, though they should be separated"

That's kind of weaselly though. A more correct version at

282 281 279 283 characters

@p=(Thir,Four,Fif,Six,Seven,Eigh,Nine);@n=("\x8",One,Two,Three,Four,Five,@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]":Zero}$p+=<>;warn$m=n($p)," is ",$_=$p-4?n$p=()=$m=~/\w/g:magic,".\n"until/c/
1

Python:

#!/usr/bin/env python

# Number of letters in each part, we don't count spaces
Decades = ( 0, 3, 6, 6, 6, 5, 5, 7, 6, 6, 0 )
Smalls  = ( 0, 3, 3, 5, 4, 4, 3, 5, 5, 4 )
Teens  =  ( 6, 6, 8, 8, 7, 7, 9, 8, 8 )

def Count(n):
    if n > 10 and n < 20: return Teens[n-11]
    return   Smalls[n % 10 ] + Decades [ n / 10 ]

N = input()

while N-4:
    Cnt = Count(N)
    print "%d is %d" % ( N, Cnt)
    N = Cnt

print "4 is magic"
Vlad
  • 9,180
  • 5
  • 48
  • 67
  • 4
    I like it. You could probably tighten it up a bit though. – Josh K Jul 12 '10 at 19:05
  • @Vlad: Input should be read from stdin instead from the arguments. That means you could just use `N = input()` (or `raw_input()`) and eliminate the `sys` stuff. – kennytm Jul 12 '10 at 19:08
  • Also you could make smalls include teens, then the if statement would only be "if n < 20: return Smalls[n]". Smalls would still work for the >= 20 case, because of the modulus by 10. – Jon Smock Jul 12 '10 at 19:27
  • 5
    This must be the first time I see the (fully optional) `she-bang` in a code-golf answer ;-) – ChristopheD Jul 12 '10 at 19:28
  • Looks like a good start... Definitely tighten it up, even Python does not need ALL of this whitespace. :-) Also, as Ferruccio points out, 0 doesn't work, specifically it seems to go into an infinite loop. – Platinum Azure Jul 12 '10 at 21:01
  • @ChristopheD: Ruby parses the shebang by itself so there it *can* make a difference. No other language does that, as far as I'm aware, though. – Joey Jul 12 '10 at 23:38
1

Ruby, 164 characters

n=gets.to_i;s="03354435543668877987";if n==0;puts"0 is 4.";else;puts"#{n} is #{n=(n<20)?s[n]-48:"0066555766"[n/10]-48+s[n%10]-48}." until n==4;end;puts"4 is magic."

decoded:

n = gets.to_i
s = "03354435543668877987"
if n == 0
  puts "0 is 4."
else
  puts "#{n} is #{n = (n < 20) ? s[n] - 48 : "0066555766"[n / 10] - 48 + s[n % 10] - 48}." until n == 4
end

puts "4 is magic."
Jon Smock
  • 9,451
  • 10
  • 46
  • 49
1

C++, 171 characters (#include omitted)

void main(){char x,y,*a="03354435543668877988";scanf("%d",&x);for(;x-4;x=y)y=x?x<19?a[x]-48:"_466555766"[x/10]+a[x%10]-96:4,printf("%d is %d.\n",x,y);puts("4 is magic.");}
Aaron Yodaiken
  • 19,163
  • 32
  • 103
  • 184
Tom Sirgedas
  • 3,208
  • 20
  • 17
  • I think if you consider this to be C, you can avoid the need for the `#include` because the functions will just be assumed to take `int` parameters. You can even save a stroke by making `main` return `int`. – Gabe Jul 17 '10 at 04:16
1

Lua 185 190 199

added periods, added io.read, removed ()'s on last print

 n=io.read();while(n~=4)do m=('43354435543668877988699;::9;;:699;::9;;:588:998::9588:998::9588:998::97::<;;:<<;699;::9;;:699;::9;;:'):sub(n+1):byte()-48;print(n,' is ',m,'.')n=m;end print'4 is magic.'

with line breaks

 n=io.read()
 while (n~=4) do
    m=('43354435543668877988699;::9;;:699;::9;;:588:998::9588:998::9588:998::97::<;;:<<;699;::9;;:699;::9;;:'):sub(n+1):byte()-48;
    print(n,' is ',m,'.')
    n=m;
 end 
 print'4 is magic.'
Nick Van Brunt
  • 15,244
  • 11
  • 66
  • 92
  • Needs a `n=io.read()` (+11 chars) to comply with rule to read number from standard input. Changing `print('4 is magic.')` to `print'4 is magic.'` will save 2 chars. Removing `;` after `)` will save 1 char. The `print` using commas seems like cheating, but spec is unclear. Might as well change it to `print(n,'is',m,'.')` to save 2 chars. – gwell Jul 16 '10 at 15:41
  • Are commas rendered as newlines in Lua stand alone? It's been a while since Ive used it. – Nick Van Brunt Jul 16 '10 at 20:41
  • The commas are rendered as tabs. – gwell Jul 17 '10 at 00:03
0

PhP Code

function get_num_name($num){  
    switch($num){  
        case 1:return 'one';  
    case 2:return 'two';  
    case 3:return 'three';  
    case 4:return 'four';  
    case 5:return 'five';  
    case 6:return 'six';  
    case 7:return 'seven';  
    case 8:return 'eight';  
    case 9:return 'nine';  
    }  
}  

function num_to_words($number, $real_name, $decimal_digit, $decimal_name){  
    $res = '';  
    $real = 0;  
    $decimal = 0;  

    if($number == 0)  
        return 'Zero'.(($real_name == '')?'':' '.$real_name);  
    if($number >= 0){  
        $real = floor($number);  
        $decimal = number_format($number - $real, $decimal_digit, '.', ',');  
    }else{  
        $real = ceil($number) * (-1);  
        $number = abs($number);  
        $decimal = number_format($number - $real, $decimal_digit, '.', ',');  
    }  
    $decimal = substr($decimal, strpos($decimal, '.') +1);  

    $unit_name[1] = 'thousand';  
    $unit_name[2] = 'million';  
    $unit_name[3] = 'billion';  
    $unit_name[4] = 'trillion';  

    $packet = array();    

    $number = strrev($real);  
    $packet = str_split($number,3);  

    for($i=0;$i<count($packet);$i++){  
        $tmp = strrev($packet[$i]);  
        $unit = $unit_name[$i];  
        if((int)$tmp == 0)  
            continue;  
        $tmp_res = '';  
        if(strlen($tmp) >= 2){  
            $tmp_proc = substr($tmp,-2);  
            switch($tmp_proc){  
                case '10':  
                    $tmp_res = 'ten';  
                    break;  
                case '11':  
                    $tmp_res = 'eleven';  
                    break;  
                case '12':  
                    $tmp_res = 'twelve';  
                    break;  
                case '13':  
                    $tmp_res = 'thirteen';  
                    break;  
                case '15':  
                    $tmp_res = 'fifteen';  
                    break;  
                case '20':  
                    $tmp_res = 'twenty';  
                    break;  
                case '30':  
                    $tmp_res = 'thirty';  
                    break;  
                case '40':  
                    $tmp_res = 'forty';  
                    break;  
                case '50':  
                    $tmp_res = 'fifty';  
                    break;  
                case '70':  
                    $tmp_res = 'seventy';  
                    break;  
                case '80':  
                    $tmp_res = 'eighty';  
                    break;  
                default:  
                    $tmp_begin = substr($tmp_proc,0,1);  
                    $tmp_end = substr($tmp_proc,1,1);  

                    if($tmp_begin == '1')  
                        $tmp_res = get_num_name($tmp_end).'teen';  
                    elseif($tmp_begin == '0')  
                        $tmp_res = get_num_name($tmp_end);  
                    elseif($tmp_end == '0')  
                        $tmp_res = get_num_name($tmp_begin).'ty';  
                    else{  
                        if($tmp_begin == '2')  
                            $tmp_res = 'twenty';  
                        elseif($tmp_begin == '3')  
                            $tmp_res = 'thirty';  
                        elseif($tmp_begin == '4')  
                            $tmp_res = 'forty';  
                        elseif($tmp_begin == '5')  
                            $tmp_res = 'fifty';  
                        elseif($tmp_begin == '6')  
                            $tmp_res = 'sixty';  
                        elseif($tmp_begin == '7')  
                            $tmp_res = 'seventy';  
                        elseif($tmp_begin == '8')  
                            $tmp_res = 'eighty';  
                        elseif($tmp_begin == '9')  
                            $tmp_res = 'ninety';  

                        $tmp_res = $tmp_res.' '.get_num_name($tmp_end);  
                    }  
                    break;  
            }  

            if(strlen($tmp) == 3){  
                $tmp_begin = substr($tmp,0,1);  

                $space = '';  
                if(substr($tmp_res,0,1) != ' ' && $tmp_res != '')  
                    $space = ' ';  

                if($tmp_begin != 0){  
                    if($tmp_begin != '0'){  
                        if($tmp_res != '')  
                            $tmp_res = 'and'.$space.$tmp_res;  
                    }  
                    $tmp_res = get_num_name($tmp_begin).' hundred'.$space.$tmp_res;  
                }  
            }  
        }else  
            $tmp_res = get_num_name($tmp);  
        $space = '';  
        if(substr($res,0,1) != ' ' && $res != '')  
            $space = ' ';  
        $res = $tmp_res.' '.$unit.$space.$res;  
    }  

    $space = '';  
    if(substr($res,-1) != ' ' && $res != '')  
        $space = ' ';  

    if($res)  
        $res .= $space.$real_name.(($real > 1 && $real_name != '')?'s':'');  

    if($decimal > 0)  
        $res .= ' '.num_to_words($decimal, '', 0, '').' '.$decimal_name.(($decimal > 1 && $decimal_name != '')?'s':'');  
    return ucfirst($res);  
}  

//////////// testing ////////////////

 $str2num = 12;
    while($str2num!=4){
        $str = num_to_words($str2num, '', 0, '');  
        $str2num = strlen($str)-1;
        echo $str . '=' . $str2num .'<br/>';
        if ($str2num == 4)
            echo 'four is magic';
    }

////// Results /////////

Twelve =6
Six =3
Three =5
Five =4
four is magic
Developer
  • 25,073
  • 20
  • 81
  • 128
0

Perl - 130 chars


5.12.1   (130 chars) 121 123 132 136 140

#        1         2         3         4         5         6         7         8         9        100        11        12        13       14    
#23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123

@u='4335443554366887798866555766'=~/./g;$_=pop;say"$_ is ",$_=$_-4?$_<20?$u[$_]:$u[$_/10+18]+(($_%=10)&&$u[$_]):magic,"."until/\D/


5.10.1   (134 chars) 125 127 136 140 144

#        1         2         3         4         5         6         7         8         9        100        11        12        13       14    
#23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234

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


Change History:

20100714:2223 - reverted change at the attention of mobrule, but ($_%10&&$u[$_%10])(($_%=10)&&$u[$_]), which is the same # of chars, but I did it in case someone might see a way to improve it

20100714:0041 - split//,'...''...'=~/./g
20100714:0025 - ($_%10&&$u[$_%10])$u[$_%10]
20100713:2340 - while$_until/\D/ + removed unnecessary parentheses
20100713:xxxx - $=<>;chop;$_=pop; - courtesy to mobrule


Note: I was tired of improving others' answers in comments, so now I'm being greedy and can just add my changes here :) This is a split off from Platinum Azure's answer - credit in part to Hobbs, mobrule, and Platinum Azure.

Community
  • 1
  • 1
vol7ron
  • 40,809
  • 21
  • 119
  • 172
  • When you got rid of the `$_%10&&...` construct, you broke the spec for inputs 20,30,40,... – mob Jul 14 '10 at 15:16
  • +1 Nicely done. You've gone out of stdin into args though :-( – Platinum Azure Jul 14 '10 at 15:22
  • Right, replaced with `ARGV`, which is populated by `STDIN` :) or.. `echo bar | xargs perl foo.pl`, technically piped from echo into args for perl :) – vol7ron Jul 15 '10 at 03:32
0

Shameless Perl with Number Words (329 characters)

Adapted fairly directly from P Daddy's C code, with some tweaks to p() to make it do the same thing using Perl primitives instead of C ones, and a mostly-rewritten mainloop. See his for an explanation. Newlines are all optional.

@t=(qw(zero one two three four five six sM eight nine
tL elM twelve NP 4P fifP 6P 7P 8O 9P twLQ NQ forQ fifQ
6Q 7Q 8y 9Q en evL thir eL tO ty 4SmagicT)," is ",".\n");
sub p{local$_=$t[pop];1while s/[0-Z]/$t[-48+ord$&]/e;
print;length}$_=<>;chop;while($_-4){
$_=($_>19?(p($_/10+18),$_&&print("-"),$_%=10)[0]:0)+p$_;
p 35;p$_;p 36}p 34

Side note: it's too bad that perl print just returns true/false; if it returned a count it would save me 7 strokes.

Nas Banov
  • 28,347
  • 6
  • 48
  • 67
hobbs
  • 223,387
  • 19
  • 210
  • 288
0

Ruby, 141 chars:

n=gets.to_i;m="4335443554366887798866555766";loop{s=n;n=n>20?m[18+n/10]+m[n%10]-96: m[n]-48;puts"#{s} is #{n==s ? 'magic': n}.";n==s &&break}
Krzysztof
  • 464
  • 2
  • 4
-7
while(true)
{
    string a;
    ReadLine(a)
    WriteLine(4);

}
Na7coldwater
  • 1,404
  • 12
  • 24
user368038
  • 265
  • 1
  • 5
  • 16