17
\$\begingroup\$

Welcome to the world of compiler golf. Your task is to write a program that generates another program to play a variant of FizzBuzz on spec.

Your compiler

Write a compiler that generates variants of the FizzBuzz program to spec. The spec of this variant is expressed in the form of an array of integer/string pairs.

  • The input may be in any form that is convenient to your language. (My examples use n:xxxx, but this is just for illustrative purposes.)
  • Each integer input may only be used once per invocation of your compiler.
  • The integer of each pair will have a value of at least one.
  • The string of each pair will be made of only exactly four ASCII letters.
  • The output must be a single complete program that conforms to the rules below.
  • The output may be in any convenient form, as long as it is a textual program. (So no returning lambda expressions.)

Behavior is undefined for inputs not conforming to the above rules.

Your generated FizzBuzz program

The program generated by your compiler will take a single integer, n, as input. It will output a sequence of numbers starting from one up to and including n, replacing numbers with FizzBuzz strings when required.

  • The generated program must be in the same language as the compiler.
  • The input n may be in any form convenient to your language.
  • n will have a value of at least one.
  • A number that is a multiple of at least one of the integers input to the compiler must be replaced by all of the strings paired with those integers joined together.
  • A number that is not to be replaced by a FizzBuzz string must be output in decimal ASCII.

For example;

> GenFizzBuzz 3:Fizz 5:Buzz
> a.out 5
1
2
Fizz
4
Buzz

Scoring

Your entry will be scored by the length of the programs your compiler generates added to the length of your compiler. Run your compiler many times with each the following parameters and add the lengths of the generated programs together with the length of the compiler to find your score.

  1. Just Count. (No inputs - The generated program will count 1 to n without replacements.)
  2. Just Golf. (1:Golf - The generated program will output "Golf" n times.)
  3. Classic FizzBuzz. (3:Fizz, 5:Buzz)

(Note that your compiler is required to generate code for any valid input, not just these listed.)

\$\endgroup\$
  • \$\begingroup\$ no scoring for the length of the compiler?? \$\endgroup\$ – Sparr Jul 31 '14 at 14:30
  • \$\begingroup\$ can we assume the integers are single digit? that there are no spaces in the strings? \$\endgroup\$ – Sparr Jul 31 '14 at 14:52
  • \$\begingroup\$ @Sparr Would that (two digit integers) make a difference? Remember, its only the code generated that makes your score. \$\endgroup\$ – billpg Jul 31 '14 at 15:04
  • \$\begingroup\$ well, fizzbuzz is an already extremely thoroughly golfed problem elsewhere on the internet. I don't know if I could forget reading the solution to it if I tried. \$\endgroup\$ – Sparr Jul 31 '14 at 15:09
  • 1
    \$\begingroup\$ Finally a golf challenge that actually makes sense to write in AWK. \$\endgroup\$ – shadowtalker Jul 31 '14 at 16:33

13 Answers 13

8
\$\begingroup\$

Python 3 - 168 162 + 230 = 392

Oh, Python, you try so hard, but multiplying the import sys;sys.argv stuff by 4 really hurts!

import sys;a=eval(sys.argv[1])
print("import sys\nfor i in range(1,int(sys.argv[1])+1):print("+"+".join('"%s"*(i%%%d==0)'%t for t in a)+(a and"or str(i))"or"i)"))

Output programs:

import sys
for i in range(1,int(sys.argv[1])+1):print(i)
import sys
for i in range(1,int(sys.argv[1])+1):print("Golf"*(i%1==0)or str(i))
import sys
for i in range(1,int(sys.argv[1])+1):print("Fizz"*(i%3==0)+"Buzz"*(i%5==0)or str(i))
  • Expected input for the main program is an eval-able sequence of Python tuples or '()' for no input. (You did say "convenient".) Example input: '()', '("Golf",1),', '("Fizz",3),("Buzz",5)' Note quoting for shell and trailing comma for one input.

  • Fixed 1am mistake by changing from dict (undefined ordering!) to tuples.

  • Expected input for the other programs is just the number

\$\endgroup\$
  • \$\begingroup\$ In your example command line argument, I had to wrap in double quotes and use single quotes for 'Fizz' and 'Buzz' - like so `"{3:'Fizz',5:'Buzz'}" however the program is still throwing an error for me. \$\endgroup\$ – James Williams Aug 1 '14 at 8:56
  • \$\begingroup\$ What is the error? \$\endgroup\$ – Jason S Aug 1 '14 at 14:13
  • \$\begingroup\$ @JasonS - Hi there. I'm interested in your experience of this challenge. meta.codegolf.stackexchange.com/questions/5050/… \$\endgroup\$ – billpg Apr 9 '15 at 17:06
6
\$\begingroup\$

perl6 376 340 84+115=199

UPDATE: switched from perl5 to perl6 to get say without use feature.

UPDATE: three test cases instead of five

There are hundreds of already-golfed solutions to FizzBuzz, and many contests end with the same result, so that's where I started. My compiler just produces a customized version of that solution. A few extra characters were inserted to account for the "just count" variation.

compiler, expects arguments like so: "Fizz 3" "Buzz 5"

print'say(('.(join'.',map{'('.(join')[$_%',split).']'}@ARGV).')||$_)for 1..$ARGV[0]'

compiled programs, expect argument like so: 100

say(()||$_)for 1..$ARGV[0]
say(((Golf)[$_%1])||$_)for 1..$ARGV[0]
say(((Fizz)[$_%3].(Buzz)[$_%5])||$_)for 1..$ARGV[0]

compiled programs for old test cases:

say(((Twoo)[$_%2].(Four)[$_%4].(Eiht)[$_%8])||$_)for 1..$ARGV[0]
say(((Twoo)[$_%2].(Thre)[$_%3].(Five)[$_%5].(Sevn)[$_%7])||$_)for 1..$ARGV[0]
\$\endgroup\$
  • \$\begingroup\$ I've changed the rules as discussed in the question's comments. You'll want to recalculate your score. \$\endgroup\$ – billpg Jul 31 '14 at 15:37
  • \$\begingroup\$ @billpg done, and improved :) \$\endgroup\$ – Sparr Jul 31 '14 at 15:52
  • \$\begingroup\$ Hi there. I'm interested in your experience of this challenge. meta.codegolf.stackexchange.com/questions/5050/… \$\endgroup\$ – billpg Apr 9 '15 at 17:05
3
\$\begingroup\$

Pyth - 51 + (38 + 43 + 50) = 182 bytes

Can probably golf the compiler a few bytes. The links on all of them are permalinks to the online interpreter.

Compiler - 51 bytes

%"K[%s)=dc\"%s\"dFGr1hQJkFNKI!%%GN~J@dxKN))?JJG",zw

Just does string formatting with an input tuple. Takes input like:

3 5
Fizz Buzz

Nothing - 38 bytes

K[)=dc""dFGr1hQJkFNKI!%GN~J@dxKN))?JJG

Just Golf- 43 bytes

K[1)=dc"Golf"dFGr1hQJkFNKI!%GN~J@dxKN))?JJG

Classic Fizz Buzz - 50 bytes

K[3 5)=dc"Fizz Buzz"dFGr1hQJkFNKI!%GN~J@dxKN))?JJG
\$\endgroup\$
2
\$\begingroup\$

C++11 ~ 486 + (234 + 244 + 255) = 1219

First participation here, this challenge is not among the most difficult ones so I thought I'd give it a try. Using C++ though, and even with C++11 additions it is still a pretty verbose language, but I'm sure there's room for improvement.

Compiler (486):

#include<sstream>
#include<iostream>
using namespace std;main(int c,char**v){stringstream t;int i;string s,o;o="#include <iostream>\n#include <map>\nusing namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{";int z=2;for(int j=1;j<c;++j){t.str(v[j]);t.clear();t >> i; t >> s;o+="{"+to_string(i)+",\""+s+"\"}"+(z++==c?"":",");}o+= R"(};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}})";cout<<o;}

It assumes arguments in the form of 3Fizz 5Buzz etc.

Count (234):

#include <iostream>
#include <map>
using namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}}

Golf (244):

#include <iostream>
#include <map>
using namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{{1,"Golf"}};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}}

FizzBuzz (255):

#include <iostream>
#include <map>
using namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{{3,"Fizz"},{5,"Buzz"}};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}}

Additional information

Tested with GCC 4.8.1, no compiler cheats.

Here is a small makefile to automate the generation of the test cases and run them (use make run):

run:
    g++ main.cpp --std=c++11 -o fbc

    ./fbc > count.cpp
    g++ count.cpp --std=c++11
    echo "======= Count ========"
    ./a.out 15

    ./fbc 1Golf > golf.cpp
    g++ golf.cpp --std=c++11
    echo "======= Golf ========"
    ./a.out 15

    ./fbc 3Fizz 5Buzz > fizzbuzz.cpp
    g++ fizzbuzz.cpp --std=c++11
    echo "======= FizzBuzz ========"
    ./a.out 15
\$\endgroup\$
  • \$\begingroup\$ Hi there. I'm interested in your experience of this challenge. meta.codegolf.stackexchange.com/questions/5050/… \$\endgroup\$ – billpg Apr 9 '15 at 17:06
  • \$\begingroup\$ map<int,string> f could be map<int,string>f. You could initialize j=1 at the same time with z. \$\endgroup\$ – Yytsi Sep 24 '16 at 7:35
2
\$\begingroup\$

Ruby 99 + (86 + 94 + 103) = 382

puts"(1..ARGV[0].to_i).each{|i|x=[];#{ARGV[0]}.each{|k,v|x<<v if i%k==0};puts x.size>0?x.join():i}"

Usage:

wc -c main.rb # 99 chars
ruby main.rb "{}" | ruby - 100 # 1..2..3..
ruby main.rb "{}" | wc -c # 86 chars
ruby main.rb "{1=>:Golf}" | ruby - 100 # Golf..Golf..Golf..
ruby main.rb "{1=>:Golf}" | wc -c # 94 chars
ruby main.rb "{3=>:Fizz,5=>:Buzz}" | ruby - 100 # 1..2..Fizz..4..Buzz..
ruby main.rb "{3=>:Fizz,5=>:Buzz}" | wc -c # 103 chars
\$\endgroup\$
2
\$\begingroup\$

Stax, 23+5+17+29=74

╥╟.└ç╘SJ∞CF╔v=▌╝Σ@∞ìé«g

Run and debug it

Shortest answer so far Not surprisingly beaten by Jelly. The string template provided in Stax is really neat and provides printf-like functions. The programs generated by the compiler are almost always as short as the best one can achieve by manually codegolfing, without using packing.

The compiler itself is 23 bytes long.

The ASCII equivalent is:

{H34|S_h"_`c%z`n?+"m"mz`cc_?

Provided input [], generates this one (5 bytes)

mzc_?

Run and debug it

Provided input [[1,"Golf"]], generates this one (17 bytes)

mz_1%z"Golf"?+c_?

Run and debug it

Provided input [[3,"Fizz"],[5,"Buzz"]], generates this one (29 bytes)

mz_3%z"Fizz"?+_5%z"Buzz"?+c_?

Run and debug it

\$\endgroup\$
1
\$\begingroup\$

Common Lisp, 636 577

(ql:quickload'cl-ppcre)(lambda(z)(princ(subseq(ppcre:regex-replace-all" *([(')]) *"(with-output-to-string(@)(print`(lambda(n)(dotimes(i n)(loop for(m s)in ',z if(=(mod(1+ i)m)0)do(princ s))(do()((fresh-line))(princ (1+ i)))))@))"\\1")1)))

I took my other answer and wrapped it in quasiquotes while adding input parameters. I print the resulting form as a single-line and remove unnecessary whitespace characters. The compiler is a little longer than the previous version, but the resulting score is reduced.

Score

(let ((*standard-output* (make-broadcast-stream)))
  (loop
     for form in '(215                      ; Compiler
                   ()                       ; Count
                   ((1 "Golf"))             ; Golf
                   ((3 "Fizz")(5 "Buzz")))  ; FizzBuzz
     for length = (if (numberp form) form
                      (length (funcall *fun* form)))
     collect length into lengths
     sum length into sum
     finally (return (values sum lengths))))

Returned values:

574
(215 111 119 129)

Pretty

(defun fizz-buzz-compiler (z)
  (princ (subseq
          (cl-ppcre:regex-replace-all
           " *([(')]) *"
           (with-output-to-string (stream)
             (print
              `(lambda (n)
                 (dotimes(i n)
                   (loop for (m s) in ',z
                      if (=(mod(1+ i)m)0)
                      do (princ s))
                   (do () ((fresh-line))
                     (princ (1+ i))))) stream))
             "\\1") 1)))

The input format is a list of (number string) couples. For example:

(fizz-buzz-compiler '((3 "Fizz")(5 "Buzz")))

... prints to standard output:

(LAMBDA(N)(DOTIMES(I N)(LOOP FOR(M S)IN'((3 "Fizz")(5 "Buzz"))IF(=(MOD(1+ I)M)0)DO(PRINC S))(DO NIL((FRESH-LINE))(PRINC(1+ I)))))

... which, pretty-printed, is:

(lambda (n)
  (dotimes (i n)
    (loop for (m s) in '((3 "Fizz") (5 "Buzz"))
          if (= (mod (1+ i) m) 0)
          do (princ s))
    (do () ((fresh-line)) (princ (1+ i)))))

Testing the resulting function:

CL-USER> ((lambda (n)
  (dotimes (i n)
    (loop for (m s) in '((3 "Fizz") (5 "Buzz"))
          if (= (mod (1+ i) m) 0)
          do (princ s))
    (do () ((fresh-line)) (princ (1+ i))))) 20)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
\$\endgroup\$
1
\$\begingroup\$

Perl 5, 77 + 93, 170 bytes

say"say",(map{++$i%2?"'$_":"'x!(\$_%$_)".($i<$#F?'.':'||$_')}@F),' for 1..<>'

Try the compiler online!
Try just count online!
Try just golf online!
Try fizz buzz online!

\$\endgroup\$
1
\$\begingroup\$

Jelly, 88 84 83 73 bytes

Shortest answer so far (beats the previous "shortest answer" by 1 byte)

Compiler:

Ṿ€“ḍ@€“ẋ"ЀF€ȯ"”jµFF?⁾RY

Try it online! (compiler)

Try it online! (verify the bytecount)


Statistics:

19 24 compiler
   20 golf
17  2 count
   27 fizzbuzz
83 73 total
\$\endgroup\$
  • \$\begingroup\$ Nice to see being beaten. \$\endgroup\$ – Weijun Zhou Apr 14 '18 at 8:05
0
\$\begingroup\$

C, 1080 bytes total

Compiler [369 bytes]

#include<stdlib.h>
r,t,f=3,b=5,n;char*F="FIzz",*B="buZZ";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

Fizz Buzz [241]

#include<stdlib.h>
r,t,f=3,b=5,n;char*F="FIzz",*B="buZZ";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

Golf [237]

#include<stdlib.h>
r,t,f=1,b=0,n;char*F="golf",*B="";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

Count [233 bytes]

#include<stdlib.h>
r,t,f=0,b=1,n;char*F="",*B="";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}
\$\endgroup\$
0
\$\begingroup\$

dc, 434 bytes

[:a]sa[91Pn93Pznlanps_znlanz0<R]sR[[[lj1-;aP1sb]sB0sj[dljd2+sj;a%0=Bljlz>F]sF[p2Q]sP]P]sI[[[]sF[pq]sP]nq]sN[z0=Nzn[sz]PlRxlIx]x[sn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx]P

Try it online!

Input for the compiler (168 bytes) should be placed on the stack as integer, string, integer, string, and so on (3 [Fizz] 5 [Buzz]). It should be given in the order one wants their fizzes and buzzes to be printed, which may be a bit of a cheat (having implemented bubble sort in dc before, I believe it would cost me around 100 bytes) but it also allows the user to, say, still have 'Fizz' run on 3 and 'Buzz' run on 5, but have 15 yield 'BuzzFizz'.

I'm sure this can be golfed a bit more; the main macro in the final program (M) relies on two macros (F and P) that are rather unnecessary given no input. Right now the compiler checks for input and outputs different (much smaller) versions of these macros if there is none, but I'm not sure the whole setup is optimal.

The compiler itself is pretty straightforward, it just checks to see if there are 'rules' on the stack, and if so it prints code that stores the stack depth in z, stores the stack in a 0-indexed array a, and then prints the generalized FizzBuzz code. If there was nothing on the stack, it really just prints a modified version of the FizzBuzz code. Test cases:

No input (46 bytes):

[]sF[pq]sPsn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx

3[Fizz]5[Buzz] (117 bytes):

4sz[Buzz]3:a5
2:a[Fizz]1:a3
0:a[lj1-;aP1sb]sB0sj[dljd2+sj;a%0=Bljlz>F]sF[p2Q]sPsn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx

1[Golf] (103 bytes):

2sz[Golf]1:a1
0:a[lj1-;aP1sb]sB0sj[dljd2+sj;a%0=Bljlz>F]sF[p2Q]sPsn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx

They all expect the n value on the stack, this gets stored in n. The ones that have 'rules' place them in the array a, with the strings at odd indices and the integers at evens. The main macro, M, increments whatever is on the stack, runs macro F which checks the value against array a, checks whether F set register b to truthy or not and prints the top of stack if so or a newline if not, resets b to falsy, and then keeps running itself if n hasn't been reached yet. Macro F, given rules, goes through the whole array looking for matches. It increments by two since our integers and strings are woven through the array, and on a match it calls macro B. Macro B just retrieves the string (current position in array less one), and prints it. It also sets b to truthy. Our compiler doesn't bother to print B for no input, and essentially makes F a nop.

\$\endgroup\$
0
\$\begingroup\$

vim, 122 (compiler) + 73 (empty) + 90 (golf) + 123 (fizzbuzz) = 392 bytes

Compiler

:%s/\v(.*):(.*)/qq\1jA\2<C-V><C-V><C-V><ESC>q=@qgg
VgggJAddGdd:%s/\v[0-9]*([^0-9])/\1
<C-V><ESC>:%@n
:w
:so! %
<ESC>ggii%s/=/<C-V><ESC><C-V><C-A>a/g<C-V><ESC>"ncc:%!seq 0 =
<ESC>

Input format

3:Fizz
5:Buzz

Generated code for FizzBuzz case

i%s/=/<ESC><C-A>a/g<ESC>"ncc:%!seq 0 =
qq3jAFizz<C-V><ESC>q=@qggqq5jABuzz<C-V><ESC>q=@qggddGdd:%s/\v[0-9]*([^0-9])/\1
<ESC>:%@n
:w
:so! %

Generated Code, Annotated

# replace the input number with a regex that replaces the placeholder (=) 
# with the real number + 1 (we'll need an extra line as a terminator later)
i%s/=/<ESC><C-A>a/g<ESC>

# pull the substitution command into register c and enter insert mode
"ncc

# create the numbers 0..N+1
:%!seq 0 =

# for each word, scan down k lines at a time and append the word to each
qq3jAFizz<C-V><ESC>q=@qgg
qq5jABuzz<C-V><ESC>q=@qgg

# delete the 0 and N+1 lines
ddGdd

# remove the numbers from any line with words
:%s/\v[0-9]*([^0-9])/\1
<ESC>

# Run the command we created at the beginning, replacing the placeholder 
# with the real number
:%@n

# The file now contains yet another program, with the constants defined.   
# Save and run.
:w
:so! %

# The file now contains a program that, when run on a buffer containing 
# a single line with a number, will produce the appropriate output

<C-V> is 0x16. <ESC> is 0x1b. <C-A> is 0x01.

Example session

$ cat code.txt
2:Foo
4:Bar
$ cat input.txt
8
$ { cat compile.vim; echo ':wq'; } | vim code.txt
# code.txt now contains the generated code
$ { cat code.txt; echo ':wq'; } | vim input.txt
$ cat input.txt
1
Foo
3
FooBar
5
Foo
7
FooBar
\$\endgroup\$
  • \$\begingroup\$ There's something odd happening when I try to define and run a macro from within another macro. If I manage to sort that out, I might save a few bytes over the save and source approach. \$\endgroup\$ – Ray Apr 4 '18 at 21:23
-2
\$\begingroup\$

SlooSarksi .Lang, 179

%%--43^jjk"/][][0[#!#111# h SD G ergDFGdfg[]9--99+==
\$\endgroup\$
  • 10
    \$\begingroup\$ I'm not familiar with this language; could you link us to a page describing it? \$\endgroup\$ – lirtosiast Oct 5 '15 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.