Zig NEWS

loading...

Fizz Buzz

Sobeston
・3 min read

Let's start playing with Zig by solving a problem together.

Fizz buzz is a game where you count upwards from one. If the current number isn't divisible by five or three, the number is said. If the current number is divisible by three, "Fizz" is said; if the number is divisible by five, "Buzz" is said. And if the number is divisible by both three and five, "Fizz Buzz" is said.

Starting

Let's make a new file called fizz_buzz.zig and fill it with the following, which has been taken from our hello_world.zig code. This provides us with an entry point and a way to print to the console. For now we will take for granted that our stdout writer works.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

}
Enter fullscreen mode Exit fullscreen mode

Here, const is used to store the value returned by getStdOut().writer(). We may use var to declare a variable instead; const denotes immutability. We'll want a variable that stores what number we're currently at, so let's call it count and set it to one.

    const stdout = std.io.getStdOut().writer();
    var count = 1;
Enter fullscreen mode Exit fullscreen mode

In Zig there is no default integer type for your programs; what languages normally call "int" does not exist in Zig. What we've done here is made our count variable have the type of comptime_int. As the name suggests, these integers may only be manipulated at compile time which renders them useless for our uses. When working with integers in Zig you must choose the size and signedness of your integers. Here we'll make count an unsigned 8-bit integer, where the u in u8 means unsigned, and i is for signed.

    const stdout = std.io.getStdOut().writer();
    var count: u8 = 1;
Enter fullscreen mode Exit fullscreen mode

What we'll do next is introduce a loop, from 1 to 100. This while loop is made up of three components: a condition, a continue expression, and a body, where the continue expression is what is executed upon continuing in the loop (whether via the continue keyword or otherwise).

    var count: u8 = 1;
    while (count <= 100) : (count += 1) {

    }
Enter fullscreen mode Exit fullscreen mode

Here we'll print all numbers from 1 to 100 (inclusive). The first argument of print is a format string and the second argument is the data. Our usage of print here outputs the value of count followed by a newline.

    var count: u8 = 1;
    while (count <= 100) : (count += 1) {
        try stdout.print("{}\n", .{count});
    }
Enter fullscreen mode Exit fullscreen mode

Now we can should test count for being multiples of three or five, using if statements. Here we'll introduce the % operator, which performs modulus division between a numerator and denometer. When a % b equals zero, we know that a is a multiple of b.

    var count: u8 = 1;
    while (count <= 100) : (count += 1) {
        if (count % 3 == 0 and count % 5 == 0) {
            try stdout.writeAll("Fizz Buzz\n");
        } else if (count % 5 == 0) {
            try stdout.writeAll("Buzz\n");
        } else if (count % 3 == 0) {
            try stdout.writeAll("Fizz\n");
        } else {
            try stdout.print("{}\n", .{count});
        }
    }
Enter fullscreen mode Exit fullscreen mode

Modulus division is more complicated with a signed numerator.

Using a Switch

We can also write this using a switch over an integer. Here we're using @boolToInt which converts bool values into a u1 value (i.e. a 1 bit unsigned integer). You may notice that we haven't given div_5 an explicit type - this is because it is inferred from the value that is assigned to it. We have however given div_3 a type; this is as integers may widen to larger ones, meaning that they may coerce to larger integer types providing that the larger integer type has at least the same range as the smaller integer type. We have done this so that the operation div_3 * 2 + div_5 provides us a u2 value, or enough to fit two booleans.

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    var count: u8 = 1;

    while (count <= 100) : (count += 1) {
        const div_3: u2 = @boolToInt(count % 3 == 0);
        const div_5 = @boolToInt(count % 5 == 0);

        switch (div_3 * 2 + div_5) {
            0b10 => try stdout.writeAll("Fizz\n"),
            0b11 => try stdout.writeAll("Fizz Buzz\n"),
            0b01 => try stdout.writeAll("Buzz\n"),
            0b00 => try stdout.print("{}\n", .{count}),
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We can rewrite the switch value to use bitwise operations. This is equivalent to the operation performed above.

switch (div_3 << 1 | div_5) {
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

Here you've successfully written two Fizz Buzz programs using some of Zig's basic arithmetic and control flow primitives. Hopefully you feel introduced to the basics of writing Zig code. Don't worry if you didn't understand it all.

Discussion (2)

Collapse
gonzus profile image
Gonzalo Diethelm

May I suggest you write the loop as while (count <= 100) ...? This way, you have one less magic number (101) and it is blindingly obvious you are looping to 100 inclusive.

Cheers!

Collapse
sobeston profile image
Sobeston Author

That's a fair improvement, I've updated it.