\$\begingroup\$

///, 128 bytes

/:/fABCDEFGHIJKLMNOPQRSTUVWXYZ fbfbAfxf xbA_xf_x xfbbbAfbb//x/bff//f/\///b/\\:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:

Try it online!

Inspired by Jakube's amazing answer to the L-phabet challenge, I thought I'd try my hand as well at actual programming in /// as opposed to just using it for compression. This was pretty tricky and I needed four attempts, but in the end it came out much shorter than my compression-based solution.

Explanation

A quick primer on ///: basically the interpreter just reads the code character by character and does the following:

If it's neither a \ nor a / , print it.

nor a , print it. If it's a \ , print the next character.

, print the next character. If it's a / , parse a /x/y/ instruction (with the same escaping rules) and repeatedly substitute all x in the remaining code with y .

Taking some more inspiration from Jakube, for simplicity I'll just explain a 4x4 version of this:

/:/fABCD fbfbAfxf xbA_xf_x xfbbbAfbb//x/bff//f/\///b/\\:B:C:D:

We start by replacing those : with the stuff between the second and third / . This will end up being the code the rotates the subsequent rows. We get this:

/x/bff//f/\///b/\\fABCD fbfbAfxf xbA_xf_x xfbbbAfbbBfABCD fbfbAfxf xbA_xf_x xfbbbAfbbCfABCD fbfbAfxf xbA_xf_x xfbbbAfbbDfABCD fbfbAfxf xbA_xf_x xfbbbAfbb

The f , b and x are just shorthands for common strings, which we'll expand now. The f is for slashes, the b is for backslashes and the x is for \// which happens to come up quite a lot. The reason I'm using aliases for the single-character substrings / and \ is that they'd have to be escaped in the first substitution instruction, so I'm actually saving quite a lot of bytes by not needing all those backslashes. Here's what we get after x , f and b have been filled in:

ABCD /\/\A/\/// \//\A_\///_\// \///\\\A/\\B/ABCD /\/\A/\/// \//\A_\///_\// \///\\\A/\\C/ABCD /\/\A/\/// \//\A_\///_\// \///\\\A/\\D/ABCD /\/\A/\/// \//\A_\///_\// \///\\\A/\\

Very readable.

So the first line is just printed verbatim. Then we get to the funky part that rotates all further rows. It actually consists of four different instructions. One thing to notice is that I've escaped all occurrences of A within these instructions. The reason for this is that it allows me to distinguish A s within the instructions from A s in the remaining rows, which need to be processed differently.

/\/\A/\//

This matches /A and replaces it with / , removing the A . Note that this substring only appears at the front of each ABCD , so this drops the first character of all subsequent lines:

/ \//\A_\//

This matches a linefeed followed by a slash and replaces it with A_/ . So this inserts an A at the end of each line, completing the rotation and also turns the linefeed into an underscore.

/_\// \//

This matches _/ and replaces it with a linefeed followed by a slash. The reason I need to make this detour via the underscore is the fact that /// applies each instruction repeatedly until the string no longer matches. That means you can never use an instruction of the form /x/axb/ where x , a and b are arbitrary strings, because after the substitution x will always still match. In particular, this means we can't just insert something in front of a linefeed. We need to replace the linefeed in the process and the undo this replacement.

/\\\A/\\B/

This matches \A and replaces it with \B , so that the instructions after the remaining rows process the next character. After all four instructions have been processed the remaining string looks like this:

BCDA /\/\B/\/// \//\B_\///_\// \///\\\B/\\C/BCDA /\/\B/\/// \//\B_\///_\// \///\\\B/\\D/BCDA /\/\B/\/// \//\B_\///_\// \///\\\B/\\

So now the first rotated row gets printed, and then the next set of instructions rotates the remaining rows by another cell and so on. After the last rotation, we have a few more instructions that we can ignore and then we end with the incomplete instruction:

/\\\B/\\

Incomplete instructions at the end are simply ignored and the program terminates.