><>, 53 bytes
"+ ␞␞"r4[r}:}>1-$:\
<~x<1 }-1}o:}+^?:$o/oa;!?:{
^ \-
(Replace ␞
with the corresponding byte 0x1E; it is replaced with a printable unicode character here for clarity.)
Try it online! The first +
always starts at position 30.
Explanation
Initialization and Iteration Cleanup
"+ ␞␞"r4[r}:}
Let's examine this section bit-by-bit, as it's a bit more complicated than a simple stack initialization.
"+ ␞␞"
This initializes the stack with [43, 32, 30, 30]
, which are the [OutputChar, PadChar, InitialPosition, IterationCount]
respectively.
r4[r
This is a no-op on the first iteration, but on each next iteration, it only retrieves the bottom-most four entries on the stack. Our method of repeating the iteration here is to simply return to the first line, which will re-push the initial string. We want to ignore this on every subsequent iteration, hence these four characters. (While I could remove them and simply jump to the corresponding position on line 1, this should require more bytes, given the rather delecate structure of the code.)
}:}
The last thing that needs to be done before our output loop is to move the iteration count to the bottom of the stack (}
), and save a copy of the current horizontal position to the bottom of the stack (:}
).
Horizontal Position Output
>1-$:\
^?:$o/
This is a relatively simple loop. We first subtract 1 from the horizontal position (1-
), output a copy of our padding character ($:o$
, which is split across the mirrors to save bytes), and, if the counter has reached zero, we skip repeating this loop; otherwise, we repeat this process (:?
).
Post-loop procedures
+ \
<~x<1 }-1}o:}+ /oa;!?:{
^ \-
I've kept in only the bits necessary to illustrate the function of the more delicate part of the code. Starting at the +
in the middle, going left, we do a few things. We discard the 0 counter we do not need anymore (+
), output a copy of the snake character (}:o
), subtract 1 from the copy of the iteration counter we saved earlier (}1-
), and then we prepare to do the random procedure }1
. This pushes a 1 to the stack, which we will randomly add, subtract, or ignore relative to the horizontal position we saved earlier.
+
<~x<1
^ \-
Remember the +
is part of the initialization string from earlier. The x
instruction is ><> source of randomness; it chooses a random direction and sets the IP to go that way. This means we have four options: left, right, up, and down. If it goes right, it simply encounters the <
instruction and we are redirected back to the x
, once again obtaining a random direction. Effectively, we limit the random directions to be only left, up, or down.
If we go left, we'll simply pop the 1; the horizontal position does not change. We then continue left and wrap around to the rest of the post-loop procedures.
If we go up, we encounter the +
from earlier, adding 1 to the horizontal position. We will then continue up and wrap around to the mirror, reflecting left, then following the arrows to the rest of the post-loop procedures.
If we go down, we find that same mirror, but reflect right instead, encountering the -
, and subtracting 1 from the horizontal position. We continue and wrap around to the right, following the arrows as before and continuing on.
\
/oa;!?:{
After modifying the horizontal position, we now want to examine the loop counter ({
). If it is 0, we terminate (:?!;
). Otherwise, we continue on, outputting a newline (ao
), hitting the mirror from before, going down, wrapping around, hitting the other mirror, and going to the left. This brings us back to the very start of the code, and the process begins again.