--Boundary-00wYWGFyZ4Djt9UP
Content-Type: Multipart/Mixed;
  boundaryoundary-00wYWGFyZ4Djt9UP"

--Boundary-00wYWGFyZ4Djt9UP
Content-Type: text/plain;
  charsettf-8"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

My solution uses a simple recursive method to build a turtle-graphics string
for the fractal. I use three commands:

 F: move forward one unit
 L: turn left, but do not move
 R: turn right, but do not move

So level 1 can be represented by "FLFRFRFLF". I thought of doing it more
compactly and letting L & R imply a move, so that level 1 would be
"FLRRL", but that made some of my code more complex, so I stuck with the
verbose way.

My code (in fractals.rb) can accept different level-0 and level-1 turtle
strings, so it can be used to build other fractals besides the one in the
quiz. The basic algorithm I use is:

  For every char in level-1:
    If the char is the level-0 char, insert level-n-1, else insert the char

This is a bit different than:

  For every level-n-1 in self, insert self

But it seems to be equivalent. So the turtle-string-building code is pretty
short. Most of my code is for converting that string into different output
formats. These include the turtle string itself, text like that in the
original quiz email (turtle_text.rb), displayed on the screen using RMagick
(non-Windows only, if I remember the docs correctly), and any graphic format
that RMagick can write (turtle_draw.rb and turtle_image.rb).

Here's some examples:

$ ./fractals.rb 2
        _
      _| |_
    _|     |_
  _|_       _|_
_| |_|     |_| |_

$ ./fractals.rb 3 -format display # fractal display with ImageMagick

$ ./fractals.rb 3 -format png
Wrote level-3 fractal to fractal_F_FLFRFRFLF_3.png

$ ./fractals.rb 3 -l1 FLFFRF # alternate level-1 rule
   _
  |
 _|
|_ _
    |
    |_ _
       _|      _               _
      |       | |_ _          | |_ _
     _|    _ _|     |      _ _|    _|
    |_ _  |         |_ _  |       |
        |_|             |_|      _|
                                |_ _
                                    |
                                    |_ _
                                       _|
                                      |
                                     _|

(See the comment at the top of fractals.rb for the full usage.)

The RMagick stuff went pretty well, but the one thing that annoyed me was that
setting Draw's affine transformation matrix does not affect previous drawing
calls like line(). So I have to trace over the entire fractal once beforehand
just to get the size of it to set up the affine.

Also, for my TurtleText.textize method, I wanted to be able to use
Turtle.each_coord, and convert graphics coords to text coords. I couldn't find
a nice way to do that, though. For example, here are the widths and heights of
some fractals using graphics and text:

Turtle  Wg Wt  Hg Ht
F        1  1   0  1
LF       0  1   1  1

So Wt (text width) can't be calculated using only Wg, and neither for Ht and
Hg. This led to some kind-of duplication of code spirit, rather than letter,
in textize() and each_coord(). I think that, for the fractal in the quiz,
the widths and heights for level L are:

Wt: 2*3**L - 1
Ht: (3**L / 2).ceil
Wg: 3**L
Hg: (3**L / 2).floor

Though since I wanted to be general for other fractals, I couldn't use these.

It was fun to play around with different base strings, and see the resulting
image. Here's a few interesting ones I found (you can infer the rules from the
filenames):

http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_FFL_10.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_FLFRF_6.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_RFRFF_8.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_FLFLFR_10.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_LFFFRFLFLF_4.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_RFRFLFF_6.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_FLFRFRFLF_4.png
http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_LFFF_6.png

-- 
Jesse Merriman
jessemerriman / warpmail.net
http://www.jessemerriman.com/

--Boundary-00wYWGFyZ4Djt9UP
Content-Type: application/x-ruby;
  nameractals.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filenameractals.rb"

#!/usr/bin/env ruby
# Ruby Quiz 125: Fractals
# fractals.rb
#
# Usage: ./fractals.rb LEVEL [-l0 LEVEL_0] [-l1 LEVEL_1] \
#                      [-format turtle | text | display | IMG_FORMAT]
#
# LEVEL: an integer > .
# LEVEL_0 and LEVEL_1: turtle-graphics strings describing the first two levels
#                      of the fractal (defaults: F & FLFRFRFLF).
#
# Formats (default: text):
#   turtle:     Print out a turtle-graphics string of Fs, Ls, and Rs.
#   text:       Print out a textual representation of the fractal.
#   display:    Use RMagick to display the image.
#   IMG_FORMAT: An image format like png, jpg, or gif that RMagick understands.

DefaultLevel0  F'
DefaultLevel1  FLFRFRFLF'
DefaultFormat  text'

class Fractal
  # Create a new fractal from the givel level_0 and level_1 turtle strings.
  def initialize level_0, level_1
    @level_0  evel_0
    @level_0_byte  evel_0[0] # Just to keep from re-calculating it over & over
    @level_1  evel_1
    self
  end

  # Return the turtle string for drawing the fractal to the given level.
  def turtle level
    if level.zero?   then @level_0
    elsif level 1 then @level_1
    else
      s  '
      @level_1.each_byte do |b|
        b @level_0_byte ? s + urtle(level-1) : s + .chr
      end
      s
    end
  end
end

if __FILE__ $0
  # Read arguments.
  level  RGV.first.to_i

  i  RGV.index '-l0'
  lev0  i.nil? ? DefaultLevel0 : ARGV[i+1].upcase)
  i  RGV.index '-l1'
  lev1  i.nil? ? DefaultLevel1 : ARGV[i+1].upcase)

  i  RGV.index '-format'
  format  i.nil? ? DefaultFormat : ARGV[i+1])

  # Build the turtle-graphics string of the fractal.
  turtle  ractal.new(lev0, lev1).turtle level

  # Output.
  if format 'turtle'
    puts turtle
  elsif format 'text'
    require 'turtle_text'
    puts TurtleText.textize(turtle)
  else
    require 'turtle_image'
    image  urtleImage.new turtle
    if format 'display'
      image.display
    else
      begin
        filename  fractal_#{lev0}_#{lev1}_#{level}.#{format}"
        image.write filename
        puts "Wrote level-#{level} fractal to #{filename}"
      rescue Exception ex
        $stderr.puts "There was an error writing the image to #{filename}"
        $stderr.puts "Are you sure the format '#{format}' is valid?"
        $stderr.puts "(#{ex})"
      end
    end
  end
end

--Boundary-00wYWGFyZ4Djt9UP
Content-Type: application/x-ruby;
  nameurtle_text.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filenameurtle_text.rb"

# Ruby Quiz 125: Fractals
# turtle_text.rb

require 'turtle'

# Contains the textize method for creating a text representation of a turtle
# graphic.
module TurtleText
  BG           '
  Vertical    |'
  Horizontal  _'

  # Returns a string representation for the given turtle graphics string.
  # Too bad Turtle.each_coord doesn't seem to be usable here, what with the
  # special x & y adjustments and such.
  def TurtleText.textize turtle
    x, y, grid  , 0, []
    last_abs_dir  il

    # These two derement procs will automatically shift things up or right if
    # the values try to go below zero.
    decrement_x  ambda do
      if x > 0 then x - 
      else grid.each { |row| row.insert(0, nil) } end
    end
    decrement_y  ambda do
      if y > 0 then y - 
      else grid.insert 0, [] end
    end

    Turtle.each_absolute_direction(turtle) do |abs_dir|
      grid[y]  ] if grid[y].nil? # Hmm.. any way to give an array a default?

      if abs_dir Turtle::Up
        grid[y][x]  ertical
        y + 
      elsif abs_dir Turtle::Right
        x +  if not last_abs_dir.nil?
        grid[y][x]  orizontal
        x + 
      elsif abs_dir Turtle::Down
        decrement_y[]
        grid[y][x]  ertical
      else # abs_dir Turtle::Left
        decrement_x[] if not last_abs_dir.nil?
        grid[y][x]  orizontal
        decrement_x[]
      end

      last_abs_dir  bs_dir
    end

    # Convert grid to a string, reversing first since its upside-down.
    grid.reverse.inject('') do |str, row|
      str + row.map { |v| v.nil? ? BG : v }.join + "\n" 
    end.chomp
  end
end

--Boundary-00wYWGFyZ4Djt9UP
Content-Type: application/x-ruby;
  nameurtle_draw.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filenameurtle_draw.rb"

# Ruby Quiz 125: Fractals
# turtle_draw.rb

require 'turtle'
require 'RMagick'

class TurtleDraw < Magick::Draw
  StrokeLength  0 # pixels

  attr_reader :min_x, :min_y, :max_x, :max_y

  # Create a new TurtleDraw from the given string. x_ and y_translation are
  # extra (Cartesian) translations for the affine transform.
  def initialize turtle, x_translation  , y_translation  
    super()
    setup_attributes
    setup_affine turtle, x_translation, y_translation
    draw_turtle turtle
    self
  end

  # Setup all graphic attributes.
  def setup_attributes
    fill_opacity    0
    stroke          'gold3'
    stroke_width    2
    stroke_linecap  'round'
    stroke_linejoin 'round'
  end

  # The affine transform needs to do two things:
  #   - flip the coordinate system so y increases upwords (Cartesian)
  #   - shift to keep everything in the positive quadrant
  def setup_affine turtle, x_translation  , y_translation  
    min, max  urtle.corners turtle
    @min_x, @min_y  in.map! { |v| StrokeLength * v }
    @max_x, @max_y  ax.map! { |v| StrokeLength * v }
    affine 1, 0, 0, -1, -@min_x + x_translation , @max_y + y_translation
  end

  # Draw the given turtle string, setting the corner coords along the way.
  def draw_turtle turtle
    last_x  ast_y  
    Turtle.each_coord(turtle, false) do |x, y|
      x, y   * StrokeLength, y * StrokeLength
      line last_x, last_y, x, y
      last_x, last_y  , y
    end
  end
end

--Boundary-00wYWGFyZ4Djt9UP
Content-Type: application/x-ruby;
  nameurtle_image.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filenameurtle_image.rb"

# Ruby Quiz 125: Fractals
# turtle_image.rb

require 'turtle_draw'
require 'RMagick'

# This class isn't really necessary, what with TurtleDraw doing the hard work.
# It just sets up some attributes and uses a TurtleDraw to draw on itself.
class TurtleImage < Magick::Image
  BGColor  black'
  Border   0 # pixels

  # Create a new TurtleImage from the given string.
  def initialize turtle
    draw  urtleDraw.new turtle, Border, Border
    width   raw.max_x - draw.min_x + 2*Border
    height  raw.max_y - draw.min_y + 2*Border
    super(width, height) { self.background_color  GColor }
    draw.draw self
    self
  end
end

--Boundary-00wYWGFyZ4Djt9UP
Content-Type: application/x-ruby;
  nameurtle.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filenameurtle.rb"

# Ruby Quiz 125: Fractals
# turtle.rb

# Miscellaneous turtle-graphics stuff.
module Turtle
  Up, Left, Right, Down  0..3).to_a # absolute directions

  # Yield once for each absolute direction moved in when following the given
  # turtle string. abs_dir is the initial absolute direction.
  def Turtle.each_absolute_direction turtle, abs_dir  ight
    turtle.each_byte do |b|
      rel_dir  .chr
      # Perhaps some trig would be better than all these ifs?
      if abs_dir Up
        if    rel_dir 'L' then abs_dir  eft
        elsif rel_dir 'R' then abs_dir  ight end
      elsif abs_dir Right
        if    rel_dir 'L' then abs_dir  p
        elsif rel_dir 'R' then abs_dir  own end
      elsif abs_dir Down
        if    rel_dir 'L' then abs_dir  ight
        elsif rel_dir 'R' then abs_dir  eft end
      else # abs_dir Left
        if    rel_dir 'L' then abs_dir  own
        elsif rel_dir 'R' then abs_dir  p end
      end

      yield(abs_dir) if rel_dir 'F'
    end
  end

  # Return an array of two arrays, each containing the coords of the lower-left
  # corner and the upper-right corner of the given turtle string.The initial
  # coordinate is [0, 0]. Each segment has a length of 1.
  def Turtle.corners turtle
    min_x  ax_x  in_y  ax_y  
    last_x  ast_y  
    each_coord(turtle, false) do |x, y|
      min_x   if x < min_x; max_x   if x > max_x
      min_y   if y < min_y; max_y   if y > max_y
      last_x, last_y  , y
    end
    [[min_x, min_y], [max_x, max_y]]
  end

  # Yield once for each x,y coordinate for the given string. The coord is an
  # array of two numbers. The initial coordinate is [0, 0]. Each segment has a
  # length of 1.
  def Turtle.each_coord turtle, yield_first  rue
    abs_dir  ight
    x    
    yield([x, y]) if yield_first

    each_absolute_direction(turtle) do |abs_dir|
      if    abs_dir Up    then y + 
      elsif abs_dir Right then x + 
      elsif abs_dir Down  then y - 
      else                        x -  end # abs_dir Left
      yield [x, y]
    end
  end
end

--Boundary-00wYWGFyZ4Djt9UP--
--Boundary-00wYWGFyZ4Djt9UP--