Code Golf: Four is magic
Asked Answered
B

30

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.

Bouchier answered 12/7, 2010 at 18:12 Comment(30)
How high a number should we handle? < 100? < 1000? < 1000000? < 2**31?Aramen
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.Tronna
@P Daddy...part 6 says only 0-99.Tronna
@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.Bouchier
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)Febrile
Spoiler: Data for 0 - 99: pastie.org/1041288Schlemiel
easysurf.cc/cnvert18.htm - look at the source code here for numbers up to 15 digits long.Febrile
@Tommy: Probably, but the challenge is how to do all that in as few characters as you can! This is code golf, after all. :-)Bouchier
I would think hard coding each element would take up more room than a if/mod/division statement :)Febrile
See also: https://mcmap.net/q/112562/-code-golf-number-to-wordsSteelhead
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.Bouchier
Character count or octet (byte) count for the shortest solution?Diophantus
@Johannes Rössel: Characters. :-)Bouchier
Is it allowed to have lines like 4 is 4. before the line saying 4 is magic.?Pennell
@Ventero: Nope. Should go straight to 4 is magic.Bouchier
4 is only magic because it was chosen by a fair dice roll.Dimarco
Seems a bit language specific to handle the input as well.Saprolite
@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?Bouchier
You're clearly giving an advantage to Lisp, though ;)Diophantus
@Jahannes: I didn't know it had that functionality :-(Bouchier
This has to be one of my favorite Code Golf challenges ever.Daffie
@Jason: Thanks! :-) I just wish people who are downvoting or voting to close would come forward and explain why. :-(Bouchier
Ignore them. They're apparently unable to find the »ignore tags« feature.Diophantus
@Platinum - well PHP would have to take $_POST vars, java has to create reader boilerplate, perl can just have it piped in etc.Saprolite
@Graphain: These are just reasons some languages are better suited to Code Golf than others.Aramen
@P Daddy it just seems like an evener ground is found by saying "take input, return output" in a programattic sense. MehSaprolite
@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.Bouchier
"reliably proctor this puzzle yourself." What???Longing
@Ash: By which I mean, you can test someone else with it now that you know the transformation function.Bouchier
Code golf has been discussed on meta several times. I stand by my position that it doesn't belong, but defer to the consensus; I also stand by my feeling on what makes a good one 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.Thermograph
L
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."
Laniferous answered 12/7, 2010 at 18:12 Comment(11)
why is this so far down? it's shorter than the lisp one and doesn't use a built-in formatting functionAuckland
I like how the code ends with "magic.", it pretty much sums it up.Gummite
@Aistina: That's easy to do in this challenge, I think. :-)Bouchier
@Aistina: haha, that is kinda funny. "mumbo jumbo yada yada..magic"Obituary
GolfScript should be considered a hack. I'm glad it beats Lisp, but in the real rules of Golf, there is no GolfScript allowed :)Obituary
That string looks awfully familiar. ;) What's the "d" at the end for?Aramen
@P Daddy The d is extracted by the ) as 100 and is used as the radix for the base conversion.Laniferous
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)Bouchier
I love how this doesn't actually contain the words "one, two, three, four" etc like the other answers do.Telephonic
@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.Obituary
@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.Auckland
S
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
Steelhead answered 12/7, 2010 at 18:12 Comment(5)
@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: p3rl.org/popYouthen
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)Obituary
A bit longer, but creativity goes a long way in my book.Tronna
@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?)Knighthood
@P Daddy: groan but +1 to your comment anywayBouchier
L
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."
Laniferous answered 12/7, 2010 at 18:12 Comment(11)
why is this so far down? it's shorter than the lisp one and doesn't use a built-in formatting functionAuckland
I like how the code ends with "magic.", it pretty much sums it up.Gummite
@Aistina: That's easy to do in this challenge, I think. :-)Bouchier
@Aistina: haha, that is kinda funny. "mumbo jumbo yada yada..magic"Obituary
GolfScript should be considered a hack. I'm glad it beats Lisp, but in the real rules of Golf, there is no GolfScript allowed :)Obituary
That string looks awfully familiar. ;) What's the "d" at the end for?Aramen
@P Daddy The d is extracted by the ) as 100 and is used as the radix for the base conversion.Laniferous
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)Bouchier
I love how this doesn't actually contain the words "one, two, three, four" etc like the other answers do.Telephonic
@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.Obituary
@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.Auckland
W
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.
Waikiki answered 12/7, 2010 at 18:12 Comment(14)
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)?Deadradeadweight
I thought "twenty-four" only has 10 letters?Schlemiel
The numbers after "is" should be text, too.Deadradeadweight
@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.Fard
@Mike DeSimone Feel free to edit, I like this version because it gives counts as well, making it easier to verify.Fard
I think this violates rule #2 - input must be read from standard input.Landau
@johanbev: The spec says "ignore a hyphen or spaces or "and"".Schlemiel
why is this so high up? other ones don't use a built-in format function and they are less charactersAuckland
@Auckland Because Common Lisp is awesome.Ase
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. :-)Bouchier
@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.Bouchier
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.Obituary
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.Elastic
The bonus one should properly capitalize the first word in its sentences!!! English FTW!Trenton
B
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."
Beaker answered 12/7, 2010 at 18:12 Comment(5)
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)Bouchier
@Plat: That would cause an extra space before the ..Schlemiel
@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. :-)Bouchier
Can we shorten this by using a higher base than 36?Tylertylosis
@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...Deadradeadweight
A
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.

Aramen answered 12/7, 2010 at 18:12 Comment(2)
This is easily my favorite of the word answers... Bravo, well done. +1 for you, and if I could give two checkmarks I would.Bouchier
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... =)Etamine
T
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.
Taryn answered 12/7, 2010 at 18:12 Comment(2)
Can't you just print the individual results instead of returning a table? That would make the output look nicer.Diophantus
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)ENDMicroampere
M
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. 
Mischief answered 12/7, 2010 at 18:12 Comment(11)
Where is the "4 is magic" bit? What about the "is" in the middle?Lattimer
Need to print a full stop as well.Tripura
And, it now conforms to the spec.Mischief
Okay, we need a referee call: are we defining "characters" as 'bytes stored" or "Unicode code points"?Deadradeadweight
Please choose a non-chinese refereeOleneolenka
@beli: 멩, 겻, 곋, 멩 are Korean.Schlemiel
I have to admit, I'd like to know what the byte scheme is so I know why this works... It feels vaguely unfair not to use literal bytes in this case.Bouchier
@Beaker Spies are everywhere these daysOleneolenka
My wife (a native Chinese speaker) says it's a mix of Chinese and Korean.Skewer
@belisarius: 1) She doesn't know Korean. 2) The Chinese is gibberish.Skewer
@Loren Pechtel That may become a great idea for restricting the use of ucp in golfing ... valid answers should have correct spelling ... in chinese :DOleneolenka
J
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.'
Journalize answered 12/7, 2010 at 18:12 Comment(2)
Fixed to support multiples of 10 ("ninety" rather than "ninetyzero").Microampere
Hey @Microampere :), 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 :-)Diophantus
E
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

Elastic answered 12/7, 2010 at 18:12 Comment(8)
249 without imports, class def or main def.Elastic
That's fiendish. I like the base 16. (+1)Bouchier
You could save one space by using String[]a instead of String[] a.Yokoyokohama
Thanks @Balus, also eliminated a bunch by doing simple arithmetic on the character instead of using hex parsing.Elastic
@Mark Peters: Even nastier. I feel so vanilla compared to that.Bouchier
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.Elastic
"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.Deadradeadweight
@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.Elastic
L
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.");
}
Landau answered 12/7, 2010 at 18:12 Comment(3)
Doesn't seem to work for me: ./magic 10 10 is -27. Segmentation faultCoconut
@Coconut - 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.Landau
You can save 3 characters by replacing "466555766"[n/10]+d[n%10]-96 with d[n%10]-",+++)"[n/10]Haggi
V
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.'
Veradi answered 12/7, 2010 at 18:12 Comment(0)
R
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
Ruhl answered 12/7, 2010 at 18:12 Comment(8)
int[] z would be shorter since it doesn't need the new[]Diophantus
Revised to use character arithmetic instead of array lookup.Ruhl
@mdm20: You're right. I had a mistake in the lookup table. Fixed now.Ruhl
Well, twelfth time is the charm :*DRuhl
A quicky to save 5 characters: Shorter than casting "magic" to object, would be to implicitly call ToString() on y by adding "". But, because + has higher precedence than ?:, you have to put it in the true part instead of the false part: x!=4?y+"":"magic".Aramen
I got it down to a respectable 212 by using PDaddy's suggestion, breaking up the lookup table into two parts, and converting the do while loop into while.Microampere
OK, now I got it down to 210!Microampere
Only one it seems to fail on now is #20.Jessiajessica
B
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!

Bouchier answered 12/7, 2010 at 18:12 Comment(21)
ACK! How I never tested that I'll never know.Bouchier
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.Cricoid
@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 :-)Bouchier
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.Obituary
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: gist.github.com/473289Cricoid
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.Obituary
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 137Obituary
You can also save 2 more chars : ($_%10&&$u[$_%10]) instead of ($_%10?$u[$_%10]:0) and explicit newline (1char) instead of \n (2chars)Utilize
good call on the and operator (&&)! the newline has already been mentioned - i consider golf to be one-liners, which explicit newlines violateObituary
@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.Bouchier
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.Bouchier
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.Obituary
also change that && down to &Obituary
if still using print and \n it's 145. If using say it's 141 charactersObituary
@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.Bouchier
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.Simms
@Platinum Azure: I'm not entirely sure. & acts as a bitwise operator (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 spaceObituary
mobrule brought up a good point $_==4?0:... to $_-4?...:0 to save another characterObituary
I was wrong about the bitwise-and, it was failing for values in the 90's and probably elsewhere.Obituary
@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.Bouchier
@Platinum Azure: I've already started my own. Check for my Perl answer currently at 121/125 (depending on version) :)Obituary
C
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.
Castaneda answered 12/7, 2010 at 18:12 Comment(0)
P
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
Pavier answered 12/7, 2010 at 18:12 Comment(0)
I
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);
}
Ignatius answered 12/7, 2010 at 18:12 Comment(9)
Fixed. It made it a bit shorter, too.Deadradeadweight
Nicely done. (I laughed a lot at the 20-character std conundrum!)Bouchier
Yeah, that was a real headbanger, until it dawned on me that #define would be even shorter since it could replace several tokens.Deadradeadweight
printf("is magic".\n) => puts. printf("%d",p) => puts(atoi(p)). Not only shorter but faster too.Federate
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?Deadradeadweight
Oh, and I don't think puts(atoi(p)) does what you think it does. Hint: there's no itoa().Deadradeadweight
@Mike DeSimone: I think while(p!=4) could be shortened to while(p-4). One whole character, I know, but still. :-)Bouchier
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 <stdio.h> instead of <cstdio>.Trek
@Billy a compiler that compiles code that includes <cstdio> 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.Penniepenniless
E
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."
Empressement answered 12/7, 2010 at 18:12 Comment(0)
A
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.

Aramen answered 12/7, 2010 at 18:12 Comment(0)
C
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

Coinstantaneous answered 12/7, 2010 at 18:12 Comment(0)
L
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.

Leonardoleoncavallo answered 12/7, 2010 at 18:12 Comment(1)
Yeah, got it shorter than the Java version.Waylin
V
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/
Vitovitoria answered 12/7, 2010 at 18:12 Comment(0)
P
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.'
Process answered 12/7, 2010 at 18:12 Comment(3)
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.Empressement
Are commas rendered as newlines in Lua stand alone? It's been a while since Ive used it.Collusion
The commas are rendered as tabs.Empressement
H
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.");}
Haggi answered 12/7, 2010 at 18:12 Comment(1)
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.Microampere
C
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."
Cirri answered 12/7, 2010 at 18:12 Comment(4)
Nice Ruby solution, keeping it simple. :-) (+1)Bouchier
Keeping it simple is no excuse for keeping it overly long, though ;-)Diophantus
I think you can replace 'if n==0' with 'if !n'Neelyneeoma
In Ruby? I always thought that all values except false and nil evaluated to true :-(Bouchier
S
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"
Snowclad answered 12/7, 2010 at 18:12 Comment(6)
I like it. You could probably tighten it up a bit though.Lattimer
@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.Schlemiel
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.Cirri
This must be the first time I see the (fully optional) she-bang in a code-golf answer ;-)Skier
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.Bouchier
@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.Diophantus
P
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}
Propst answered 12/7, 2010 at 18:12 Comment(0)
C
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.

Cricoid answered 12/7, 2010 at 18:12 Comment(0)
O
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.

Obituary answered 12/7, 2010 at 18:12 Comment(3)
When you got rid of the $_%10&&... construct, you broke the spec for inputs 20,30,40,...Steelhead
+1 Nicely done. You've gone out of stdin into args though :-(Bouchier
Right, replaced with ARGV, which is populated by STDIN :) or.. echo bar | xargs perl foo.pl, technically piped from echo into args for perl :)Obituary
B
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
Bucharest answered 12/7, 2010 at 18:12 Comment(3)
@wallacoloo: A crummy solution for a crummy language :DTrenton
Or a much shorter 178 characters: $l='4335443554366887798866555766';for($b=(int)fgets(fopen('php://stdin','r'));($a=$b)-4;){$b=$a<20?$l[$a]:$l[18+$a/10]+($a%10?$l[$a%10]:0);echo"$a is $b.\n";}echo"4 is magic.\n";Castaneda
Nice joke. :-D So well programmed.Engedus
V
-7
while(true)
{
    string a;
    ReadLine(a)
    WriteLine(4);

}
Vidal answered 12/7, 2010 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.