FizzBuzz without logic

FizzBuzz is an classic and very simple programming challenge. There are number of ways to make it more interesting and I decided to write FizzBuzz without any logical operators.

Why

I was watching random video about programming and realized that I wrote FizzBuzz many many times but never in C. I stopped the video and immediately started programming. It took just few minutes to get it done.

But I was not satisfied. Somehow I felt that I had too many if statements. I was right as there was one condition that could be avoided. Then it escalated quickly into: Would it be possible to avoid any conditions? I mean that really got out of hand fast. And just like that I was doomed to spent rest of the Saturday writing this code.

fizzbuzz.c

/* FizzBuzz without logical operations v2.0 by irek@gabr.pl */

#define TEN(a) a;a;a;a;a;a;a;a;a;a; /* Repeat instruction  10 times */
#define HUNDRED(a) TEN(TEN(a))      /* Repeat instruction 100 times */

/* Return 1 if NUM is 0, otherwise return 0. */
int zero(char num)
{
	static const int map[256] = {1,0};
	return map[num];
}

/* Convert NUM unsigned number that is 8-1 max digits long to string.
 * Return pointer to string. */
char *utoa(unsigned short num)
{
	static char buf[8] = {0};
	int siz = 8;
	buf[--siz] = 0;
	TEN(buf[--siz] = (num % 10) + '0'; num /= 10)
	TEN(siz += zero(buf[siz]-'0'))
	return &buf[siz];
}

/* Write STR string to standard output. */
void sput(char *str)
{
	int write(int, void*, int);
	int len = 0;
	TEN(len += zero(zero(str[len])))
	write(1, str, len);
}

int main(void)
{
	char *fizz[3] = {"fizz", "", ""};
	char *buzz[5] = {"buzz", "", "", "", ""};
	int i=0;
	HUNDRED(sput(utoa(++i));
		sput("\r");	/* Magic */
		sput(fizz[i%3]);
		sput(buzz[i%5]);
		sput("\n"))
	return 0;
}

How

There are few tricks needed to get this done.

Point 5 can be ignored because it is the easiest as we use `write(int, void*, int)` function from standard libc. It has no logical operators. Basically a system call with three arguments.

To execute code multiple times simply copy and paste code. No magic required. Or use macros like I did with TEN and HUNDRED. We need to print 100 lines of final output and in other functions we have to execute code at least 9 times to repeat math. So those two macros was all I needed. One can wander how to do the same thing in other languages.

The main strategy in code is to always do the same steps no matter what. One clever trick is the usage of `\r` ASCII character. I'm always printing the same line `"number_as_string"\r"fizz""buzz"\n`. If strings for Fizz and Buzz are empty the number will be visible. Otherwise number will be overwritten.

Converting number to string is not difficult if we restrict it to be only an positive integer. You can see it over and over that problems are being resolved by applying assumptions with restrictions and not by writing more code. For example we are always printing number from 1 to 100, numbers are always unsigned, the longest number is shorten than string "Fizz" so can be overwritten, we never have to repeat math more than 9 times etc.

I guess that code is self explanatory. What I found interesting was the zero() utility function that converts 0 to 1 and everything else to 0. With that it was easy to repeat math many times by making it loos effect after Nth step. This strategy was used to get length of the string and to get beginning of number string in utoa() function.

Written: 2023-08-11