Make FizzBuzz using only preprocessor directives

5

1

I have a source file here:

#include <FizzBuzz.h>
fizzbuzz

And I want to make "fizzbuzz" turn into a FizzBuzz program solely through the preprocessor.

Your task is to write a FizzBuzz.h that uses only preprocessor directives (things like include, define, etc.) to create a working FizzBuzz program. Specifically, I want to see the use of macros, which will be required to make this work (at minimum, defining fizzbuzz to be some code).

However, to make sure people try and take full advantage of the power of macros, your score will be defined as the sum of the cubes of the length of the definition portion (the part after #define <symbol>) of each macro.

So, for example, the following file:

#define A 123
#define B A+A
#define C B*2+A

would have a total score of 179 (3^3 + 3^3 + 5^3). As a rule, I want multiple shorter lines instead of one long line.

Joe Z.

Posted 2013-08-09T22:49:35.020

Reputation: 30 589

Do you have any reason to believe that the answers won't all define one of the tokens to an empty string and the other to a one-line program? (Or in other words, what distinguishes this from a boring "golf fizzbuzz"?) – Peter Taylor – 2013-08-09T22:52:01.717

I thought of that myself. I'll probably reupload it with new restrictions. – Joe Z. – 2013-08-09T22:52:45.563

Added a new scoring algorithm that should favour multiple short lines. – Joe Z. – 2013-08-10T17:54:33.047

Can you give an exact specification of FizzBuzz? I guess we all know the idea, but do you want a function that gets a single number, or a loop (until when?) or what? – ugoren – 2013-08-10T20:18:29.107

The same specification as here: http://www.codinghorror.com/blog/2007/02/why-cant-programmers-program.html

– Joe Z. – 2013-08-10T21:47:22.510

You need an algorithm to favor lines of code, too? How about The sum of the cubes of each definition multiplied by the number of lines in fizzbuzz.h, excluding empty lines and empty definitions.? Your example would become (3^3 + 3^3 + 5^3) * 3 for a total score of 537. – Braden Best – 2014-02-15T22:14:18.160

Answers

9

C, Score 1,283 1,213 1,205

EDIT: Instead of basing on Loki Astari's solution, now I'm based on breadbox's

The basic code is not exactly the same as breadbox's - I replaced the loop with recursion, which is longer, but has more repetition.

With GCC, it can be improved further, by using ?: instead of ?0:.

#define A "Fi"
#define D "Bu"
#define E "z"
#define L E E
#define F L,z
#define R F)
#define G A R
#define H D R
#define I ""
#define K "%d"
#define X printf
#define Y puts
#define Z main
#define M 99
#define N z%5
#define O z%3
#define P Z(z
#define Q P)
#define a Q{X
#define b a(O
#define c b?N
#define d c?K
#define e d:I
#define f e:G
#define i f;Y
#define j i(N
#define k j?I
#define l k:H
#define n l;z
#define o n++
#define p o>M
#define q p?0
#define r q:Q
#define s r;
#define fizzbuzz s}

Macro-free code:

main(z){printf(z%3?z%5?"%d":"":"Fizz",z);puts(z%5?"":"Buzz",z);z++>99?0:main(z);}

The 2nd parameter to puts does nothing. It allows me to utilize the zz",z) sequence.

ugoren

Posted 2013-08-09T22:49:35.020

Reputation: 16 527

It's a crime that this answer is the currently the lowest rated. – breadbox – 2013-08-27T23:18:51.403

@breadbox, A late answer to an unpopular question - what can you do? And it doesn't contain much new compared to other. – ugoren – 2013-08-28T04:21:35.697

6

C score 1602 1592 1535 1515 1505 1451

#include <stdio.h>
#define A       "%d"
#define B       "Bu"
#define C       "Fi"
#define w       "z"
#define D       w w
#define E       "\n"
#define F       main
#define G       printf
#define I       for
#define J       99
#define L       c++
#define O       A E
#define P       O:B
#define Q       P D
#define R       Q E
#define S       c%3
#define T       c%5
#define U       S?T
#define V       U?R
#define W       V:T
#define X       W?C
#define Y       X D
#define Z       Y E
#define a       Z:C
#define b       a D
#define d       b B
#define e       d D
#define f       e E
#define g       f,c
#define r       (g)
#define h       G r
#define i       ;L
#define j       J;h
#define k       i<=
#define l       k j
#define q       (l)
#define m       I q
#define n       m;
#define o       {n}
#define p       F()
#define s       c;p
#define fizzbuzz s o

Original source:

c;main(){for(;c++<=99;printf(c%3?c%5?"%d\n":"Buzz\n":c%5?"Fizz\n":"FizzBuzz\n",c));}

Now I am wondering if we change the code too:

c;main(){for(;c++<=99;printf(6*(c%3<1)+12*(c%5<1)+"%d  \n\0Buzz\n\0Fizz\n\0FizzBuzz\n\0",c));}

It looks longer. But more compressable using macros.

Martin York

Posted 2013-08-09T22:49:35.020

Reputation: 896

Doesn't work... c is uninitialized. Can be fixed at no cost by making c global. Even better - remove int. Even better - make it a parameter (but then it starts with 1). – ugoren – 2013-08-13T07:29:11.870

@ugoren:Fixed :-) – Martin York – 2013-08-13T07:52:18.320

You can also split some 3-char macros to 2*2. E.g. (g) -> (x + g). – ugoren – 2013-08-13T07:54:04.497

With a bit more effort it goes below 1400. – ugoren – 2013-08-13T07:57:23.917

You have 3 and 5 the wrong way round in your single-string solution. It might also be worth indexing "Buzz\n\0" as "FizzBuzz\n\0"+4. – Peter Taylor – 2013-08-14T07:33:20.850

@PeterTaylor, If the contributions of c%3 and c%5 to the pointer are additive, as in Loki's code, it won't work. But it's easily done using nested ?:. – ugoren – 2013-08-14T08:58:28.580

@ugoren, I tested it with 4*(c%3<1^c%5<1)+10*(c%5<1)+"%d\n\0Fizz\n\0FizzBuzz\n\0", although looking at it now that final \0 is unnecessary. – Peter Taylor – 2013-08-14T09:02:28.910

(I also considered how to exploit the fact that c*c*c*c%15 takes precisely 4 values in perfect correlation with the desired format string*, but I suspect that the expression gets too complicated). *For generalised FizzBuzz with primes p and q, c^lcm(p-1, q-1) mod pq distinguishes suitably. – Peter Taylor – 2013-08-14T09:06:37.600

@PeterTaylor, "Fizz\n\0------Buzz\n\0--%d\n\0FizzBuzz\n"+24-i*i*i*i%15%7*4 works. Any good? – ugoren – 2013-08-14T13:42:42.570

@PeterTaylor: The final '\0' is not there to terminate the string. It is there so that we have 4 common sub strings of '\n\0' that can be represented by a single macro. Was hoping that would make it easier to compress. – Martin York – 2013-08-14T16:03:32.430

You'd express that with two macros, though, because len("\n")^3 + len("\0")^3 = 128 < 216 = len("\n\0")^3 so the macro for just a newline would be around anyway. – Peter Taylor – 2013-08-14T16:11:29.573

@PeterTaylor: Yes agreed. But it gives opportunities for other compressions as 'Buzz\n\0' is repeated twice. Looking at in isolation is not going to help, you have to look at the big picture. Anyway it may not have worked it was just something I was thinking about. – Martin York – 2013-08-14T16:16:52.163

@LokiAstari, I have a solution with score 1,283. It copies your source exactly (except changing <= to +~) and your macro methods, so I can't publish it as my own. I hope you have the time to push your solution there. – ugoren – 2013-08-15T11:02:15.433

@ugoren: Of course you can. Just add it. – Martin York – 2013-08-15T16:06:04.883

3

C, score 1267

Best I could do:

#define a main
#define b for
#define c puts
#define d printf
#define e ""
#define f x Z
#define g y Z
#define h "%d"
#define x "Bu"
#define y "Fi"
#define z "z"
#define Z z z
#define A 101
#define B i++
#define C B%5
#define D C?e
#define E D:f
#define F i%3
#define G F?i
#define H G%5
#define I H?h
#define J I:e
#define K J:g
#define L K,i
#define M i<A
#define N M;c
#define O a(i
#define P N(E
#define Q d(L
#define R O)
#define S b(
#define T P)
#define U Q)
#define V U;
#define W R{S
#define X W;T
#define Y X)V
#define fizzbuzz Y}

This is roughly the same approach that the other entries are using, looks like. I suspect the marginal improvement comes from the brevity of the target program. At 75 bytes, it's the shortest C fizzbuzz I know (that doesn't depend on compiler-specific behavior):

main(i){for(;i<101;puts(i++%5?"":"Buzz"))printf(i%3?i%5?"%d":"":"Fizz",i);}

breadbox

Posted 2013-08-09T22:49:35.020

Reputation: 6 893

Nice, but is i's initial value guaranteed? – Daniel Lubarov – 2013-10-28T05:17:17.690

i's initial value will be one if the user invokes the program without extra command-line arguments. The behavior will change if other parameters are given on the command line, but that's true of most every program. – breadbox – 2013-10-28T05:28:13.347

2

C, Score 28,703 18,043 4,367

#define S(x) #x"\n"                     // 6
#define X(a) W?Y(a)                     // 6
#define W c%5                           // 3
#define Y(a) S(a):Z(a)                  // 9
#define Z(a) S(a##Buzz)                 // 10
#define fizzbuzz A%Q}                   // 4
#define A char*B                        // 6
#define B p,C                           // 3
#define C c;D(E                         // 5
#define D main                          // 4
#define E ){F                           // 3
#define F for(G                         // 5
#define G ;c++H                         // 5
#define H <=I(K                         // 5
#define I 99;J                          // 4
#define J printf                        // 6
#define K *p+L                          // 4
#define L ~9?M                          // 4
#define M p:N,O                         // 5
#define N S(%d)                         // 5
#define O c))P                          // 4
#define P p=c                           // 3
#define Q 3?R;                          // 4
#define R X():T                         // 5
#define T X(Fizz)                       // 7

Original macro-free program:

c,*p;
main(){
        for(;c++<=99;printf(*p+~9?p:"%d\n",c))
                p=c%3?c%5?"\n":"Buzz\n":c%5?"Fizz\n":"FizzBuzz\n";
}

ugoren

Posted 2013-08-09T22:49:35.020

Reputation: 16 527

This is the kind of solution I was looking for. :D – Joe Z. – 2013-08-12T17:59:24.793

The same macro tricks that you like are what makes my solution longer than Loki Astari's - he uses C string concatenation instead of ##. – ugoren – 2013-08-13T07:58:12.313

1

C++, Score 68,508

I know this does not meet the criteria (lots of really short macro definitions), but I think it has an advantage over the other answers. First the solution:

#include <iostream>
#define X(a) H(a,N)H(1,L)
#define W(a,b,c) H(U(a,b),c)H(a,M)
#define V J::
#define U(a,b) !(a)&&!(b)
#define T ==0
#define S K<<
#define R %5 T
#define Q main
#define P int
#define O %3 T
#define N "Buzz"
#define M "Fizz"
#define L V endl
#define K V cout
#define J std
#define I if
#define H(a,b) I(a)S(b);
#define G(a,b,c) W(a,b,c)X(b)
#define F(a) G((a)O,(a)R,a)
#define E(a,b,c,d) F(a*20+b*4+c*2+d+1)
#define D(a,b,c) E(a,b,c,0)E(a,b,c,1)
#define C(a,b) D(a,b,0)D(a,b,1)
#define B(a) C(a,0)C(a,1)C(a,2)C(a,3)C(a,4)
#define A B(0)B(1)B(2)B(3)B(4)
#define fizzbuzz P Q(){A}

Second, I think the advantage to this is that it truly uses the preprocessor to its "best" advantage. Namely, it does not use any looping constructs or variables in determining FizzBuzz. All parameters are constants, thus all the expressions can be compile time evaluated. Any if-condition that is false can be optimized away, so the final optimized form of the compiled program is really just a bunch of statements like cout << something; cout << endl.

CasaDeRobison

Posted 2013-08-09T22:49:35.020

Reputation: 736

1

C++, Score 24,806 (Revised from Earlier Answer)

I came across code I posted last September and decided to refine it. I was able to cut it down significantly (for a savings of 43,702 "points") while keeping in the spirit of my original submission (no variables, just pre-processed constants). Not that anyone cares, but hey, I was bored. :)

#include <iostream>
#define _ {A}
#define a ()
#define b main
#define c int
#define d a _
#define e c b
#define f "u"
#define g "B"
#define h "i"
#define i "F"
#define j "z"
#define k w v
#define l endl
#define m <<
#define n cout
#define o ::
#define p std
#define q g f
#define r i h
#define s j j
#define t N)O
#define u l;
#define v n m
#define w p o
#define y q s
#define z r s
#define Z(a,b) M(a)k b;
#define Y 0;
#define X return
#define W 100
#define V %5
#define U %3
#define T ==0
#define S w v w u
#define R(a) Z(a,y)
#define Q(a) Z(a,z)
#define P(a,b,c) Z(!(a)&&!(b),c)
#define O X Y
#define N >W
#define M if
#define L V T
#define K U T
#define J(a,b,c) P(a,b,c)Q(a)R(b)S
#define I(a) M(a t
#define H(a) I(a)J((a)K,(a)L,a)
#define G(a) H(a+1)H(a+2)
#define F(a) G(a)G(a+2)
#define E(a) F(a)F(a+4)
#define D(a) E(a)E(a+8)
#define C(a) D(a)D(a+16)
#define B(a) C(a)C(a+32)
#define A B(0)B(64)
#define fizzbuzz e d

CasaDeRobison

Posted 2013-08-09T22:49:35.020

Reputation: 736