Implementation notes Multiple programs interleaved through rotation. ---------------------------------------------------------------------- 0. Concept Everyone should read "A Fast Algorithm for General Raster Rotation" by Alan Paeth. It's a relatively short and easy to read paper from 1986. As soon as I learned about the 3-shear rotation algorithm, I thought it would be something fun to implement. TRICK 2025 was announced shortly after and I thought it was just the perfect opportunity to do it. Naturally, just implementing a simple rotation algorithm wasn't enough fun. What would be really fun would be to put that rotation algorithm to use, by unlocking more behavior behind rotated versions of the original rotation code. ---------------------------------------------------------------------- 1. Interleaved layout I thought a good rotation angle to use would be pi/13 radians, since it would make a good "rot13" filter, as a nod to the common Caesar cipher-based rot13 filters. pi/13 is a relatively small angle, so the rotated code will only tilt slightly. This makes the task very different from a previous project I did for IOCCC: https://uguu.org/src_nuko_c.html Nuko packs 4 programs into one, each accessible through a different rotation angle, but all rotation angles are multiples of 90 degrees. This means each program is more or less confined to a single corner, and I didn't need any special tooling to pack those programs. In comparison, an acute angle like pi/13 means the rotated programs will interleave with each other, because the first byte for each program are roughly around the same place. This makes packing multiple programs more challenging. I did the layout for Nuko entirely manually, but I had not gotten too far before deciding that the manual process wouldn't work for rotation. Some scripting to automatically generate rotated output would almost suffice, but that has not improved the process by much. The one feature I really needed was to be able to instantly see and place the text cursor across rotated versions of the text. So, I wrote a script that generates a JavaScript-based editor to do exactly that. Now that I have a tool to fill in characters and quickly see rotated results, writing interleaved text is mostly an exercise in patience. ---------------------------------------------------------------------- 2. Interleaved programs Having figured out how to interleave text, I still need to decide on what layout to use, and how to fill that layout with code. I thought about the kind of shapes that would look decent when rotated, particularly one that might look fine with different text aspect ratios. Daruma was what won out in the end -- it's the kind of object that people sometimes would tilt, and it has the rounded edges that looks decent across a range of aspect ratios. Weaving different behaviors into the rotated programs was something of a challenge, but actually I found one of them by accident. I had planned on starting the main program with the rotation angle set in the first line so that it's easily configurable: a = Math::PI / 13 Turns out, when rotated counterclockwise, the slash might end up being the first character: / 13 Math::PI a = This is great because Ruby is willing to parse // like a pattern, and silently ignore that no-op statement, which means I now have a way to splice in a piece of code for counterclockwise rotation. This was a happy accident, and I made extra effort to keep that slash in the right place in subsequent adjustments to the layout. For clockwise rotation, the key was having the equal sign at the right place, such that it creates a multi-line assignment statement when rotated: a = Math ::PI /13 Then "#" and quotes can be inserted to the left of "Math::PI/13" to splice in code. After being able to rotate the first line and have the rotated variants behave differently, what's left is now really just an exercise in patience. Though thankfully, Ruby's lenient syntax and great variety of quoting mechanisms makes this much easier than some other language. ---------------------------------------------------------------------- 3. Testing and extra features My original ambitious plan was to have 12 to 13 different pieces of code accessible after rotation, but that turned out to be infeasible because the result from applying repeated rotation just looks like scrambled eggs. So I ended up with just 3 programs, and I thought I got reasonable coverage from a small set of tests. There are some extra slack space available, so I added a brainfuck program to fill the extra space. And then, two weeks after submitting my entry, I wondered what would happen if I try the tool on input files with CR-LF end of line sequences. Turns out, Ruby treats each CR-LF sequence as a single grapheme cluster, which is great, but since I was only looking at the ASCII code for the first byte, I was just dropping those CR-LF sequences in the same way I dropped all other unsupported characters. I thought I would just fix this in the documentation ("this tool accepts only input with lines ending in LF"), especially since adding extra code disturbs the interleaved code. But with some extra golfing, I managed to squeeze in CR-LF support. I was quite pleased with myself. Actually... Ruby 3.2.2 appears to handle "\r\n" as a single grapheme cluster, but Ruby 2.7.4 does not. You can tell that this is the case by running ruby -e 'print "\r\n".grapheme_clusters.size, "\n"' And observe that Ruby 3.2.2 outputs "1" while Ruby 2.7.4 outputs "2", i.e. "\r" and "\n" are separate graphemes. I didn't notice this until a few months later. Fortunately the judges used newer version of Ruby on systems without CR-LF sequences. ---------------------------------------------------------------------- 4. Finally... This entry won first place and was awarded "most revolutionary", and I now have a small footprint inside Ruby's sample directory. It's way better than what I have expected. It has been a great honor to contribute bad example to a fun language :)