--Boundary-00 wYWGFyZ4Djt9UP Content-Type: Multipart/Mixed; boundaryoundary-00 wYWGFyZ4Djt9UP" --Boundary-00 wYWGFyZ4Djt9UP Content-Type: text/plain; charset tf-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-00 wYWGFyZ4Djt9UP 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-00 wYWGFyZ4Djt9UP Content-Type: application/x-ruby; name urtle_text.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename urtle_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-00 wYWGFyZ4Djt9UP Content-Type: application/x-ruby; name urtle_draw.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename urtle_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-00 wYWGFyZ4Djt9UP Content-Type: application/x-ruby; name urtle_image.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename urtle_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-00 wYWGFyZ4Djt9UP Content-Type: application/x-ruby; name urtle.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename urtle.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-00 wYWGFyZ4Djt9UP-- --Boundary-00 wYWGFyZ4Djt9UP--