« first day (382 days earlier)      last day (1256 days later) » 

1:32 AM
2000 words isn't too long is it?
;P
Even if you say it is, I don't care
 
1:45 AM
@AviFS I'll teach at 8:30PM utc
 
2:13 AM
hot
 
 
1 hour later…
3:16 AM
hot?
 
 
4 hours later…
7:09 AM
hot = it's very popular
 
 
13 hours later…
8:26 PM
Okay so there's probably going to be no one here for this lesson
But I'll send out a ping anyway
@petStorm @mypronounismonicareinstate @cairdcoinheringaahing @AviFS @Razetime @Dion @DLosc we're starting
Okay
Welcome to the lesson I'm calling:
18 surprising bytes you need for FizzBuzz (number 5 will shock you senseless)
Alternatively: how'd I manage to write this many words about a simple program?!
This lesson assumes:
- knowledge of stack-based languages (i.e. how stacks work)
- understanding of the fizzbuzz problem

This lesson does not assume:
- knowledge of Vyxal

By the end of this lesson, you will hopefully:
- have a basic understanding of how Vyxal works
- learn golfing techniques you can apply to other languages
- gain language design ideas
When learning a new programming language, figuring out how to write a FizzBuzz program can help learn the syntax and flow of a language: loops, conditional command execution and arithmetic are all covered while outputting the required text. Today's lesson will focus on writing FizzBuzz using Vyxal and golfing it down to 18 bytes.
FizzBuzz Mark I
Before we begin writing any code, I'll explain what Vyxal is and what it is designed for. Vyxal is my most recent esolang (which should be kind of obvious given its name), which focuses on performing well in code golf (competitions where participants try to answer questions with as short a program as possible). It is also designed to retain an aspect of "elegance" whilst remaining terse.
 
Vyxal is stack based yes?
 
@cairdcoinheringaahing yes, it is
Like most other golfing languages, the main memory model is a stack
However, there are variables and functions, which you'll be seeing within this lesson
Alright, here's a pseudocode example of what our first approach at FizzBuzz will look like:
FOR i = 1 TO 100
    IF i % 15 == 0 THEN
        PRINT "FizzBuzz"
    ELSEIF i % 5 == 0 THEN
        PRINT "Buzz"
    ELSEIF i % 3 == 0 THEN
        PRINT "Fizz"
    ELSE
        PRINT i
    ENDIF
NEXT i
It's relatively simple and the usual way one would expect to solve FizzBuzz
Now, here's the same algorithm but expressed in Vyxal:
1 101 r (i|           # FOR i = 1 TO 100
  ¥i 15 % 0 = [       #   IF i % 15 == 0 THEN
    `FizzBuzz`,       #       PRINT "FizzBuzz"
  |
    ¥i 5 % 0 = [      #   ELSEIF i % 5 == 0 THEN
      `Buzz`,         #       PRINT "Buzz"
    |
      ¥i 3 % 0 = [    #   ELSEIF i % 3 == 0 THEN
        `Fizz`,       #       PRINT "Fizz"
      |               #   ELSE
        ¥i ,          #       PRINT i
      ]
    ]
  ]
  `\n`,
)
(Yes, it is highly formatted and commented. Obviously, as we continue, we'll lose the more formal stuff)
From this, a few things are seen:
- The main control structures are delimited by brackets: this is an intentional break away from the tendency of most golfing languages to use letters to designate things like if-statements and loops
- The pipe character (|) is used to branch to different parts of the control structure (the else statement in if-statements and the loop body in for-loops)
- [...|...] is an if-statement
- (...|...) is a for loop
- Integers are their own token
- The r command generates a range between 2 integers
- Whitespace doesn't matter, and is simply a NOP
 
@Lyxal Vyxal for sure has auto-bracket completion, but do you think it's golfier in the long-term/wider scale to use a 2-byte delimiter for loops and such?
 
@cairdcoinheringaahing do you mean like [] being 2 different bytes or []] closing a loop ]] being the 2 bytes?
 
[] being 2 bytes
 
8:34 PM
If you look at 05AB1E, you can see that loops are also 2 bytes F...}, E...}, etc
So yes, I do believe it golfier
 
My guess is that something like for i = 1 to 100 { print i+1 } would be 1 101r(i|¥i1+,, which is fairly long compared to langs like 05AB1E and Jelly
 
@cairdcoinheringaahing Ah, but we're only just beginning
You'll see that things aren't always that long
 
@Lyxal Do go on :P
 
Okay, back to the list of things that occur in the first vyxal sample:
- Variables are created with £name and their values retrieved with ¥name
- Strings are delimited with backticks
- The , command prints the top of the stack
Now, seeing as how we are trying to shorten our program as much as possible, removing the whitespace and formatting gives:
1 101r(i|¥i 15%0=[`FizzBuzz`,|¥i 5%0=[`Buzz`,|¥i 3%0=[`Fizz`,|¥i ,]]]`\n`,)
(75 bytes: same length as the Mouse and Swift answer)
This program, whilst golfed, doesn't make Vyxal seem like the best language for code golf. Fortunately, I have a whole lot of tricks left to show y'all.
 
FizzBuzz Mark II
The part that seems to un-necessarily convolute our current program is the repeated usage of the loop variable i. To fix this, we can drop the explicit variable altogether and use the implicit context variable of the for loop.
Within Vyxal, the contextual variable (there is only one) is used to store values associated with different structures. In other words, the context variable has different values based upon its, well, context.
In a for loop, the context variable holds the current iteration value.
In a while loop, the context variable holds the result of the most recent evaluation of the while loop's condition.
If statements don't have a context variable assigned.
In functions and lambdas, the context variable holds the argument(s) passed to the function upon calling.
The command to retrieve the value of the context variable is n.
Using this, our program becomes:
1 101r(n15%0=[`FizzBuzz`,|n5%0=[`Buzz`,|n3%0=[`Fizz`,|n,]]]`\n`,)
(65 bytes: Just a byte longer than the Scala answer)
Any questions before we move on to the next way to shorten the program?
 
8:40 PM
FizzBuzz Mark III
Our current approach is now shorter, but it still isn't short enough to be considered competitive. What if, instead of checking 3 different modulos, we only check 3 and 5, print each respective word depending on the divisibility and then, if nothing is printed, print the index:
FOR i = 1 TO 100
    output = ""
    IF i % 3 == 0 THEN
        output += "Fizz"
    ENDIF
    IF i % 5 == 0 THEN
        output += "Buzz"
    ENDIF

    IF output == "" THEN
        PRINT i
    ELSE
        PRINT output
    ENDIF
NEXT i
Translating this to Vyxal gives:
``&1 101r(n3%0=[&`Fizz`+&]n5%0=[&`Buzz`+&]&:[,|_n,]``&`\n`,)
(60 bytes: Same length as the MAWP 2.0, IPEL and the 33 answer)
In this iteration, a few new features are introduced:

- `&` acts as a register, and toggles between popping the top of the stack into the register and emptying the register onto the stack (much like ><>)
- Empty strings are regarded as falsey when tested as a boolean value
This approach is good, but what if instead of using a flag to keep track of the output, we short-circuit or'd the register with n? Here's a table of the three possible combinations (note that n is always >0):
register      register or n

"FizzBuzz"    "FizzBuzz"
"Buzz"        "Buzz"
"Fizz"        "Fizz"
""            n
This means that we can turn &:[,|_n,] into &n⟇
Also, because our if statements only have one branch (an if statement without a pipe | consists of code to only be executed if the top of the stack is truthy), we can multiply the strings by the result of the comparison: 0 * string = empty string; 1 * string = string. Note that this wouldn't work if the result of n<>%0= had more than two possible results.
This means we turn n3%0=[&"Fizz"+&] into &"Fizz"n3%0=*+&, making our overall program:
``&1 101r(&`Fizz`n3%0=*+&&`Buzz`n5%0=*+&&n⟇``&`\n`,)
(52 bytes: Same length as the O answer)
 
FizzBuzz Mark IV
FOR I = 1 TO 100
    PUSH "Fizz"
    PUSH I
    PUSH 3
    MODULO
    PUSH 0
    EQUALS
    MULITPLY // "Fizz" * ((I % 3) == 0)
    PUSH "Buzz"
    PUSH I
    PUSH 5
    MODULO
    PUSH 0
    EQUALS
    MULITPLY // "Buzz" * ((I % 5) == 0)
    ADD
    PUSH I
    LOGICAL OR
    PRINT TOP
    PRINT "\n"
NEXT I
Thus far, we've mostly been using variables/registers to keep track of values. However, we can make our program shorter by utilising the stack mechanics of Vyxal. Consider the following pseudocode:
Translating this to Vyxal gives:
1 101r(`Fizz`n3%0=*`Buzz`n5%0=*+n⟇,`\n`,)
(41 bytes: 2 bytes shorter than my current Keg answer)
However, we're still not finished yet. For the next byte reduction, we can look at minimizing the 0= right after each modulo.
If we look at the function of `0=`, we can see that it is acting as a logical NOT operation, inverting the truthiness of the item it's comparing. Let's take a look at an example where we are testing for divisibility by 3:

When the result of `n % 3` is 0, we want to say that we DO need to multiply the string `"Fizz"`: `0 == 0 => 1`. In this way, it NOTs the `0`

Conversely, when the result of `n % 3` is 1 or 2, we want to say that we DO NOT need to multiply the string `"Fizz"`: `1 == 0 => 0` and `2 == 0 => 0`. In this way, it NOTs the truthiness of 1 and 2.
The same can be seen for checking divisibility by 5, so we can replace 0= with the logical NOT operator ¬, making our program:
1 101r(`Fizz`n3%¬*`Buzz`n5%¬*+n⟇,`\n`,)
(39 bytes: Same length as the second shortest Pyth answer, placing us in the top 30 [27th])
The savings of changing numerical comparisons to logical negation is marginal but still reduce the byte count nonetheless. But what if, instead of using any sort of boolean logic, we simply used a "is divisible" built-in:
 
Can I make a guess at the next steps? :P
 
1 101r(`Fizz`n3œ*`Buzz`n5œ*+n⟇,`\n`,)
@cairdcoinheringaahing sure
SE Chat is limiting the rate at which I send messages lol
 
1 101r is bulky, so my guess is a 2 byte compression of 101 and using a [1,2,...] range builtin. Fizz and Buzz are also long, so compression of those. `\n`, is also long, so I'm guessing there's a "print-with-newline" command
 
@cairdcoinheringaahing You're right about the string compression, but you will be surprised at how we insert the newlines as we get further down.
Remember, the answer we are working towards is 18 bytes
We're still at 37 bytes (same length as the shortest known APL FizzBuzz, placing us 25th on the leaderboard)
 
8:50 PM
FizzBuzz Mark V
37 bytes seems the shortest we can get using loops and stack manipulation. So how are we going to get the byte count down to 18?
caird suggested sticking with the looping approach, but I think we can do better than that.
Let's try using functions!
 
@Lyxal Well, I didn't suggest it, I merely didn't not suggest it :P
 
Regardless, consider the following:
BEGIN Function(index)
    PUSH "Fizz" * is_divisible(index, 3)
    PUSH "Buzz" * is_divisible(index, 5)
    ADD
    PUSH index
    LOGICAL OR
    RETURN
END

FOR I = 1 TO 100
    PRINT Function(I)
    PRINT "\n"
NEXT I
Instead of doing all the work in the loop, we can encapsulate it inside the function.
The translation is:
@f:1|`Fizz`n3œ*`Buzz`n5œ*+n⟇;1 101r(n@f;,`\n`,)
(47 bytes)
Wonderful, isn't it?
Now, you're probably wondering how it's useful when we've only made things longer.
Well, what if, instead of explicitly looping, there was a way to apply the function to each item in the range?
@f:1|`Fizz`n3œ*`Buzz`n5œ*+n⟇;1 101r°f;MIJ
(40 bytes)
In this approach, the loop has been changed into a mapping of the function to the list generated by 1 101r
It's shorter than applying the function in a for loop but still longer than a pure for loop.
Before we continue, let's quickly analyse what's happening:
@f:1|...;: This defines a function called f which takes a single parameter. Functions are defined as @name:parameters|code; and called as @name;
1 101r still produces a range between 1 and 100 (inclusive)
°f; pushes a function reference of f. Think of this as if you wrote the name of a function in python without the brackets: a = f(x) calls the function while a = f assigns the reference of function f to a.
maps the function to the range and joins it with newlines.
You may be wondering how we can reduce this program when it's clear that the function definition can't be shortened. Notice that we use f straight away, we only pass it a single parameter and that we only want a single result. If this was a language like Python, one might suggest using a lambda to eliminate the need for clunky definition syntax. So why don't we do the same in this situation:
1 101rλ`Fizz`n3œ*`Buzz`n5œ*+n⟇;MIJ
(33 bytes: 2 bytes shorter than the CJam FizzBuzz)
Here, λ...; defines a lambda (a function that is only accessible via being on the stack) and pushes its reference. Think of it like this: @name:p|... defines a function that is globally accessible and can be easily reused hundreds of times. λ...; pushes its function onto the stack as a single encapsulated block and is only usable when its reference is on the stack or in a variable.
Finally, recall that the context variable for lambdas is its argument. This means that we can easily refer to the value passed to the lambda with n.
Any questions so far?
 
FizzBuzz Mark VI
Like most golfing languages, when there aren't enough values on the stack to complete an operation, Vyxal takes implicit input and uses that instead. However, this behaviour is different when control flows moves inside a function/lambda call: rather than cycling through the global input of the program, the implicit value provided is the argument(s) passed. (Side note: functions/lambdas operate on their own local stack initialised with the values passed).
With this knowledge, we can take advantage of the stack when the argument is the only value left.
But first, we need to reorder our 33-byte program to become:
1 101rλn3œ`Fizz`*n5œ`Buzz`*+n⟇;MIJ
At the start of the lambda, n is the only value on the stack, so we can turn n3œ into because the n is already provided. We can't do that with n5œ because the result of the previous multiplication will occupy the stack.
Our program is now:
1 101rλ3œ`Fizz`*n5œ`Buzz`*+n⟇;MIJ
(32 bytes: Same length as the ESMin FizzBuzz)
Another thing we can improve is the generation of the range between 1 and 100. Vyxal, like other golfing languages, has built-ins for different ranges based on a single number: [0, n], [0, n), [1, n], [1, n). In the case of FizzBuzz, we want the range [1, 100], so we need the [1, n] range built-in. The reference page tells us that this command is ɾ. Therefore, 1 101r becomes 100ɾ.
Our full program is now:
100ɾλ3œ`Fizz`*n5œ`Buzz`*+n⟇;MIJ
(30 bytes: same length as the shortest Pyth FizzBuzz)
But 100 is such a common value that there is a 1 byte built-in for pushing the value 100:
Ĥɾλ3œ`Fizz`*n5œ`Buzz`*+n⟇;MIJ
(28 bytes: Same length as the golfing suggestion to my Charcoal FizzBuzz)
 
9:05 PM
Ah should've anticipated the 100 inclusive range, rather than 101 exclusive :P
 
Any questions?
 
Do golfing suggestions count as questions? :P
 
Not really
But I need to take a 10 minute break
After that, we'll get it down to 18 bytes
 
I know it's only 9 minutes but eh
FizzBuzz Mark VIII
(@cairdcoinheringaahing whatever golfing suggestions you had will probably be addressed)
We're still 10 bytes away from the shortest known Vyxal FizzBuzz. A quick golf that can be made is string compression: Vyxal has both dictionary and base 255 compression utilities. In this instance, we'll use dictionary compression.
Vyxal's dictionary has approximately 20,000 words that can be used within strings. Each word in the dictionary has a unique two-letter code called a "String Compression Code" (SCC). Really, the SCC is simply the index of the word in the dictionary converted to a bijective base with an alphabet based on the non-ASCII characters on the Vyxal codepage. To use an SCC, simply place it in a string. For example:
⎝Ũ, ¬Ô!
Turns into Hello, World!
Turns into Hello, World. Note that the punctuation and whitespace are retained but the SCCs are converted to their respective words. The SCCs for Fizz and Buzz are Ɗ» and ŨƇ respectively. Using this gives us:
100ɾλ3œ`Ɗ»`*n5œ`ŨƇ`*+n⟇;MIJ
(24 bytes: same length as the 05AB1E FizzBuzz)
However, this can still be improved by changing the strings involved: because we only need one SCC per string, we can utilise a feature found in other golfing languages were a single dictionary string can be constructed without the need for a closing character. ı pushes a single uncompressed SCC to the stack:
Ĥɾλ3œıƊ»*n5œıŨƇ*+n⟇;MIJ
(22 bytes: same length as the MathGolf FizzBuzz)
And I just realised I accidentally missed vii
As in misnumbered the heading
 
9:22 PM
Fizzbuzz Mark Viii (really 8)
At this stage, we are 2 bytes away from tying with Jelly. This byte reduction can be achieved through the introduction of a new structure: lambda maps. A lambda map is a lambda that instantly maps its function to the top of the stack, removing the need for an explicit M. Its structure is ƛcode; and is equivalent to λcode;M (fun fact, the parser turns the lambda map into a lambda token followed by the M command - that's how equivalent it is). We can turn our program into:
Ĥɾƛ3œıƊ»*n5œıŨƇ*+n⟇;IJ
Another special function of the lambda map is that, when its argument is an integer, it maps its function over the range [0, n]...obviously, mapping over an integer doesn't make sense, and it would be a waste of features to have mapping on an integer error. Therefore, we can write:
Ĥƛ3œıƊ»*n5œıŨƇ*+n⟇;IJ
 
@Lyxal 3 really, with the latest version of Jelly, but go on :P
 
FizzBuzz Mark IX (the last one I swear)
What we have works for 20 bytes except for the fact that it actually isn't a valid answer. It maps from 0 to 100 not 1 to 100. Luckily, there's a fix for this that doesn't require the addition of bytes: command-line flags. (We're getting into some contested territory now, so fasten your seatbelts!)
When the Vyxal interpreter is run on the command line with the h flag and no other file, a list of all available flags is printed to the console:
Usage: python3 Vyxal.py <file> <flags (single string of flags)> <input(s) (if not from STDIN)>
ALL flags should be used as is (no '-' prefix)
        j       Print top of stack joined by newlines
        L       Print top of stack joined by newlines(Vertically)
        s       Sum/concatenate top of stack on execution
        M       Use 1-indexed range [1,n] for mapping integers
        m       Use 0-indexed range [0,n) for mapping integers
        v       Use Vyxal encoding for input file
        c       Output compiled code
(This feature is courtesy of @Razetime: go upvote his posts and star his Github repositories please.)
The M flag looks like it will help our situation. It makes lambda maps map over the range [1, n] instead of [0, n]. Now our 20-byte answer is valid with the M flag. (I'm not going to show the program again as nothing has changed but the command-line flags).
We've tied with Jelly now (at least Dennis' answer). But I'm not done yet. Notice how there is a j flag which joins the top of the stack by newlines. We can use this to remove the IJ which explicitly joins with newlines:
Ĥƛ3œıƊ»*n5œıŨƇ*+n⟇;
(19 bytes/Mj flags)
To knock off that final byte to get to 18, we can use the auto-completion of structures. If a structure is unbalanced (e.g. ...[... or ...ƛ... gets turned into ...[...] and ...ƛ...; at parse time).
Applying this gives us:
Ĥƛ3œıƊ»*n5œıŨƇ*+n⟇
(18 bytes: unfortunately not the only 18-byte answer anymore D:)
And that is how you get FizzBuzz in 18 bytes. Hopefully, this has made some sort of sense, and hopefully, you'll be ableto apply some techniques learnt here to other golfing endeavours.
I know it may not have been the most logical path, but it should be at least somewhat comprehensible
Feel free to ask any questions.
 

« first day (382 days earlier)      last day (1256 days later) »