Context Free Art
Suppose you want to draw a shape rotated by a random angle -- let's say specifically by a random integer from 0 to 359. One approach would be to define 360 rules, each of which rotates the shape by a different angle:
rule RandomRotate { MyShape { } } rule RandomRotate { MyShape { r 1 } } rule RandomRotate { MyShape { r 2 } } rule RandomRotate { MyShape { r 3 } } rule RandomRotate { MyShape { r 4 } } /* * */ rule RandomRotate { MyShape { r 359 } }
That's a lot of rules to enter for just one effect! Fortunately, there's a better approach, based on the concept of a Search tree. The idea is first to rotate either not at all or by half a circle (i.e., either 0 or 180 degrees), then to rotate the result either not at all or by a quarter of a circle (i.e., either 0 or 90 degrees), then to rotate the result either not at all or by an eighth of a circle (i.e., either 0 or 45 degrees). At this point, we've rotated by one of 0, 45, 90, 135, 180, 225, 270, or 315 degrees -- eight possible angles from only six rules. So far, so good.
If we divide by two again we're going to start seeing non-integer angles. However, nothing says we have to keep dividing by two. For the next rotation we can rotate by one of {0, 15, 30} degrees, giving us any of 24 possible angles as illustrated below:
The last steps are to rotate by one of {0, 5, 10} degrees and finally by one of {0, 1, 2, 3, 4} degrees. In the end, it takes only 18 rules to rotate a shape by any of 360 unique angles! The rotation is also very fast: only seven rules are actually executed no matter what angle is finally chosen.
Here's a sample picture that demonstrates the preceding technique:
startshape Begin background { h 200 sat 1 b 1 } rule DrawArrow { SQUARE { x 0.5 size 1 0.075 } TRIANGLE { x 1 r 30 size 0.2 } } rule RandomRotate1 { RandomRotate2 { } } rule RandomRotate1 { RandomRotate2 { r 180 } } rule RandomRotate2 { RandomRotate3 { } } rule RandomRotate2 { RandomRotate3 { r 90 } } rule RandomRotate3 { RandomRotate4 { } } rule RandomRotate3 { RandomRotate4 { r 45 } } rule RandomRotate4 { RandomRotate5 { } } rule RandomRotate4 { RandomRotate5 { r 15 } } rule RandomRotate4 { RandomRotate5 { r 30 } } rule RandomRotate5 { RandomRotate6 { } } rule RandomRotate5 { RandomRotate6 { r 5 } } rule RandomRotate5 { RandomRotate6 { r 10 } } rule RandomRotate6 { RandomRotate7 { } } rule RandomRotate6 { RandomRotate7 { r 1 } } rule RandomRotate6 { RandomRotate7 { r 2 } } rule RandomRotate6 { RandomRotate7 { r 3 } } rule RandomRotate6 { RandomRotate7 { r 4 } } rule RandomRotate7 { DrawArrow { } } rule DrawRandomArrows { 30*{ b -0.05 z 1 } RandomRotate1 { } } rule Begin { DrawRandomArrows { h 240 sat 1 b 1 } }
Note that this technique applies just as well to selecting random colors, random distances, random tower heights, etc. as it does to random angles.
The advantages of the binary-search technique over something like
rule RandomMove 99 { RandomMove { x 1 } } rule RandomMove 1 { MyShape { } }
are
The only real disadvantage is code length.