[QUIZ] Fractals (#125)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Drew Olson

When I learned about fractals in high school math class, I immediately found
them fascinating. For those of you unfamiliar with the concept, the definition
from Wikipedia is as follows: a fractal is "a rough or fragmented geometric
shape that can be subdivided in parts, each of which is (at least approximately)
a reduced-size copy of the whole".

At the end of the unit in which we were taught them, the fractal below was a
test question. In subsequent years, I began drawing it freehand to higher and
higher levels. The details and patterns that emerge are fascinating.

The goal is to create a ruby program which takes the level as an argument and
then draws the fractal shown below to the specified level. The fractal is
created by drawing the first level, then repeating the pattern such that each
base piece is replaced with the fractal from the higher level. Thus, to move
from level 1 to level 2, we replace each line with the shape at level 1. Notice
that the position changes as well, meaning if the line is vertical we replace it
with a vertically positioned shape of level 1 (right and left facing also
matter). I have shown the first 3 levels below (including the base component at
level 0). Feel free to use the console for output or get fancy with RMagick or
something similar.

                            _ <-- Level 0
                            _
                          _| |_ <-- Level 1
                            _
                          _| |_
                        _| |_ <-- Level 2
                      _|_ _|_
                    _| |_| |_| |_
  
                            _
                          _| |_
                        _| |_
                      _|_ _|_
                    _| |_| |_| |_
                  _| |_
                _|_ _|_
              _| |_| |_| |_ <-- Level 3
            _| |_
          _|_ _|_
        _| |_|_ _ _ _|_| |_
      _| |_|_|_| |_|_|_| |_
    _|_ _|_|_ _|_|_ _|_
  _| |_| |_| |_| |_| |_| |_| |_

If you want to create an image to view instead of on the console,
RMagic/RScience may be convenient, but never forget how plain old easy
it is to make a PPM image :slight_smile:
You just write it out as text! This may be the easiest way to create
an image file from any language.

The header is 4 lines long, the data is written as r g b color values

Header:
First line is "P3"
Second line is a comment line that starts with "#" and goes however long
Third line contains two integers with a space between them, they
represent the size of the image
Fourth line is the maximum any color channel can be for any pixel.
For instance 7 will mean any pixel can have a red value 0-7, green
value of 0-7 and a blue value of 0-7. If you use 255 you're getting 8
bits of color per channel, which is what were used to now days.

The data itself:
Everything now is just an integer, separated by a \n or a space.
Every triplet of integers represents a pixel, with each integer in the
triplet representing a different channel: Red, Green, Blue.
The triplets are written in order, with no particular formatting, no
indication that a new line is starting, and no indication that the
file or data has ended. You just write them out, and it's up to the
viewer to read the header and know when the new line is, how many
integers to read, etc.
(There is a max length for any given line, and I don't remember what
it is, so be on the safe side and put in a \n after each pixel)

So this is a valid image file (1x3 pixels, one red one green one blue!)

P3
# comment line
1 3
255
255 0 0
0 255 0
0 0 255

Hope this helps some of you who want to write images, but don't want
to use a library (for some reason or another), or just don't want to
use something they don't understand.

--Kyle

PS:
http://netpbm.sourceforge.net has more info than I possibly can, and
probably describes it better, but longer.

I'm confused about the intent of this quiz. Is the main focus writing code that will re-create the ASCII art shown above, or is it just to draw the required fractal by any means of the solver's choice (for example, using the turtle geometry classes developed in Quiz 104)? If the latter is permitted, a simple, recursive turtle program will do the job.

Regards, Morton

···

On May 25, 2007, at 9:18 AM, Ruby Quiz wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Drew Olson

When I learned about fractals in high school math class, I immediately found
them fascinating. For those of you unfamiliar with the concept, the definition
from Wikipedia is as follows: a fractal is "a rough or fragmented geometric
shape that can be subdivided in parts, each of which is (at least approximately)
a reduced-size copy of the whole".

At the end of the unit in which we were taught them, the fractal below was a
test question. In subsequent years, I began drawing it freehand to higher and
higher levels. The details and patterns that emerge are fascinating.

The goal is to create a ruby program which takes the level as an argument and
then draws the fractal shown below to the specified level. The fractal is
created by drawing the first level, then repeating the pattern such that each
base piece is replaced with the fractal from the higher level. Thus, to move
from level 1 to level 2, we replace each line with the shape at level 1. Notice
that the position changes as well, meaning if the line is vertical we replace it
with a vertically positioned shape of level 1 (right and left facing also
matter). I have shown the first 3 levels below (including the base component at
level 0). Feel free to use the console for output or get fancy with RMagick or
something similar.

                            _ <-- Level 0
                            _
                          _| |_ <-- Level 1
                            _
                          _| |_
                        _| |_ <-- Level 2
                      _|_ _|_
                    _| |_| |_| |_
  
                            _
                          _| |_
                        _| |_
                      _|_ _|_
                    _| |_| |_| |_
                  _| |_
                _|_ _|_
              _| |_| |_| |_ <-- Level 3
            _| |_
          _|_ _|_
        _| |_|_ _ _ _|_| |_
      _| |_|_|_| |_|_|_| |_
    _|_ _|_|_ _|_|_ _|_
  _| |_| |_| |_| |_| |_| |_| |_

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 (2.32 KB)

turtle_text.rb (1.6 KB)

turtle_draw.rb (1.44 KB)

turtle_image.rb (639 Bytes)

turtle.rb (2.09 KB)

···

_
      _| |_
    _| |_
  _|_ _|_
_| |_| |_| |_

$ ./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/

#!/usr/bin/ruby -w

# The Fractal objects are a sequence of steps to draw
# an image. In Logo the drawing would be extremely easy.
class Fractal
  attr_reader :path

  # +level+ may be 0 or greater, and should be something like an integer.

···

On 2007-05-25 22:18:32 +0900 (Fri, May), Ruby Quiz wrote:

The goal is to create a ruby program which takes the level as an argument and
then draws the fractal shown below to the specified level. The fractal is
created by drawing the first level, then repeating the pattern such that each
base piece is replaced with the fractal from the higher level. Thus, to move
from level 1 to level 2, we replace each line with the shape at level 1. Notice
that the position changes as well, meaning if the line is vertical we replace it
with a vertically positioned shape of level 1 (right and left facing also
matter). I have shown the first 3 levels below (including the base component at
level 0). Feel free to use the console for output or get fancy with RMagick or
something similar.

                            _ <-- Level 0
                            _
                          _| |_ <-- Level 1
                            _
                          _| |_
                        _| |_ <-- Level 2
                      _|_ _|_
                    _| |_| |_| |_

  #
  # +seq+ is an array with a sequence of steps (:fw, :right, or :left) which
  # will replace each :fw (means: forward) part of the path.
  # A nice default is provided.
  def initialize level = 1, seq = nil
    super()
    @path = [:fw]
    seq = [:fw, :left, :fw, :right, :fw, :right, :fw, :left, :fw]
    level.times do
      @path.map! { |el| el == :fw ? seq.dup : el }.flatten!
    end
  end
end

# AsciiArtCanvas draws a given Fractal.path on an array of strings.
class AsciiArtCanvas

  def initialize path, initial_dir = :e
    @path = path
    @dir = initial_dir
    @canvas = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = ' ' }}
    @x = @y = @min_x = @min_y = @max_x = @max_y = 0
  end

  def paint
    @path.each { |step| step == :fw ? draw : turn(step) }
    (@min_y..@max_y).inject() do |arr,y|
      arr << (@min_x..@max_x).inject('') do |row,x|
        row + @canvas[y]
      end
    end
  end
  
  private
    def draw
      case @dir
      when :n; @y -= 1
      when :s; @y += 1
      when :e; @x += 1
      when :w; @x -= 1
      end

      @canvas[@x][@y] = case @canvas[@x][@y]
                        when '+'; '+'
                        when '-'; [:n,:s].include?( @dir ) ? '+' : '-'
                        when '|'; [:w,:e].include?( @dir ) ? '+' : '|'
                        else [:n,:s].include?( @dir ) ? '|' : '-'
                        end
      case @dir
      when :n; @y -= 1; @min_y = @y if @y < @min_y
      when :s; @y += 1; @max_y = @y if @y > @max_y
      when :e; @x += 1; @max_x = @x if @x > @max_x
      when :w; @x -= 1; @min_x = @x if @x < @min_x
      end
    end

    TURNS = {
      :n => { :left => :w, :right => :e },
      :w => { :left => :s, :right => :n },
      :s => { :left => :e, :right => :w },
      :e => { :left => :n, :right => :s },
    }
    def turn dir
      @dir = TURNS[@dir][dir]
    end
      
end

if __FILE__ == $0
  level = ARGV[0] ? ARGV[0].to_i.abs : 3

  t = Fractal.new level
  puts( *AsciiArtCanvas.new(t.path, :e).paint )
end

It's terribly inefficient for high levels:

$ time ./fract.rb 4
[...]
real 0m0.297s

for level 4: 0.297s
for level 5: 1.917s
for level 6: 37.449s
I'm too scared to run it with level 7 :slight_smile:

--
No virus found in this outgoing message.
Checked by 'grep -i virus $MESSAGE'
Trust me.

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until 48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone on Ruby Talk follow the discussion. Please reply to the
original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

···

On Fri, 25 May 2007 22:18:32 +0900, Ruby Quiz wrote:
=-=-=-=-=

by Drew Olson

When I learned about fractals in high school math class, I immediately
found them fascinating. For those of you unfamiliar with the concept,
the definition from Wikipedia is as follows: a fractal is "a rough or
fragmented geometric shape that can be subdivided in parts, each of
which is (at least approximately) a reduced-size copy of the whole".

At the end of the unit in which we were taught them, the fractal below
was a test question. In subsequent years, I began drawing it freehand to
higher and higher levels. The details and patterns that emerge are
fascinating.

The goal is to create a ruby program which takes the level as an
argument and then draws the fractal shown below to the specified level.
The fractal is created by drawing the first level, then repeating the
pattern such that each base piece is replaced with the fractal from the
higher level. Thus, to move from level 1 to level 2, we replace each
line with the shape at level 1. Notice that the position changes as
well, meaning if the line is vertical we replace it with a vertically
positioned shape of level 1 (right and left facing also matter). I have
shown the first 3 levels below (including the base component at level
0). Feel free to use the console for output or get fancy with RMagick or
something similar.

                            _ <-- Level 0
                            _
                          _| |_ <-- Level 1
                            _
                          _| |_
                        _| |_ <-- Level 2
                      _|_ _|_
                    _| |_| |_| |_
  
                            _
                          _| |_
                        _| |_
                      _|_ _|_
                    _| |_| |_| |_
                  _| |_
                _|_ _|_
              _| |_| |_| |_ <-- Level 3
            _| |_
          _|_ _|_
        _| |_|_ _ _ _|_| |_
      _| |_|_|_| |_|_|_| |_
    _|_ _|_|_ _|_|_ _|_
  _| |_| |_| |_| |_| |_| |_| |_

#this is my first solution
#a turtle graphics file for use with any solution to Quiz 104

#shows the whole fractal for 0<=DEPTH<=4
#shows part of the fractal for 5<=DEPTH

DEPTH=(ARGV[1] or 3).to_i

def segment n
   if n==0
      fd [(360/(3**DEPTH)),3].max
   else
      segment n-1
      lt 90
      segment n-1
      rt 90
      segment n-1
      rt 90
      segment n-1
      lt 90
      segment n-1
   end
end

#get into a sensible home position
bk 180
rt 90
bk 180
pd
#actually draw the fractal
segment DEPTH

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until 48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone on Ruby Talk follow the discussion. Please reply to the
original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

···

On Fri, 25 May 2007 22:18:32 +0900, Ruby Quiz wrote:
=-=-=-=-=

by Drew Olson

When I learned about fractals in high school math class, I immediately
found them fascinating. For those of you unfamiliar with the concept,
the definition from Wikipedia is as follows: a fractal is "a rough or
fragmented geometric shape that can be subdivided in parts, each of
which is (at least approximately) a reduced-size copy of the whole".

At the end of the unit in which we were taught them, the fractal below
was a test question. In subsequent years, I began drawing it freehand to
higher and higher levels. The details and patterns that emerge are
fascinating.

The goal is to create a ruby program which takes the level as an
argument and then draws the fractal shown below to the specified level.
The fractal is created by drawing the first level, then repeating the
pattern such that each base piece is replaced with the fractal from the
higher level. Thus, to move from level 1 to level 2, we replace each
line with the shape at level 1. Notice that the position changes as
well, meaning if the line is vertical we replace it with a vertically
positioned shape of level 1 (right and left facing also matter). I have
shown the first 3 levels below (including the base component at level
0). Feel free to use the console for output or get fancy with RMagick or
something similar.

                            _ <-- Level 0
                            _
                          _| |_ <-- Level 1
                            _
                          _| |_
                        _| |_ <-- Level 2
                      _|_ _|_
                    _| |_| |_| |_
  
                            _
                          _| |_
                        _| |_
                      _|_ _|_
                    _| |_| |_| |_
                  _| |_
                _|_ _|_
              _| |_| |_| |_ <-- Level 3
            _| |_
          _|_ _|_
        _| |_|_ _ _ _|_| |_
      _| |_|_|_| |_|_|_| |_
    _|_ _|_|_ _|_|_ _|_
  _| |_| |_| |_| |_| |_| |_| |_

#this is my second solution. it builds on my first solution
#but reimplements the turtle graphics in ASCII art.
class Fractal
   #rotate the turtle 90 degrees to the :left or the :right
   def rotate whichway
      #lots of special cases to deal with the nature of the
      #characters used.
      case [@direction,whichway]
      when [:left,:left],[:right,:right]
   @y+=1
   @direction=:down
      when [:right,:left],[:left,:right]
   @direction=:up
      when [:down,:right]
   @x-=1
   @y-=1
   @direction=:left
      when [:up,:left]
   @x-=1
   @direction=:left
      when [:up,:right]
   @x+=1
   @direction=:right
      when [:down,:left]
   @x+=1
   @y-=1
   @direction=:right
      end
      self
   end

   #creates a blank canvas of the specified size, with the turtle in the
   #lower left corner, facing right
   def initialize width=80,height=24
      @x=0
      @y=height-1
      @direction=:right
      @matrix=Array.new(height){Array.new(width){" "}}
   end

   #move the turtle forward
   def forward
      case @direction
      when :left
   @matrix[@y][@x]="_"
   @x-=1
      when :right
   @matrix[@y][@x]="_"
   @x+=1
      when :up
   @matrix[@y][@x]="|"
   @y-=1
      when :down
   @matrix[@y][@x]="|"
   @y+=1
      end
      self
   end

   #draw a segment of the fractal
   def segment depth
      if depth==0
   forward
      else
   segment depth-1
   rotate :left
   segment depth-1
   rotate :right
   segment depth-1
   rotate :right
   segment depth-1
   rotate :left
   segment depth-1
      end
      self
   end

   #convert the matrix to a string suitable for printing
   def to_s
      @matrix.map{|row| row.join}.join("\n")
   end
end

puts Fractal.new.segment(3)

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Here is my solution to Quiz 125.

Since the required fractal is a recursive turtle path, the Turtle Geometry kit developed for Quiz 104 can be used to provide a short and simple solution to this quiz.

<code>
# Created by Morton Goldberg on May 26, 2007.
# quiz_125_fractal.rb

def fractal(n, s)
    if n == 0
       forward s
    else
       fractal(n-1, s/3.0)
       left 90
       fractal(n-1, s/3.0)
       right 90
       fractal(n-1, s/3.0)
       right 90
       fractal(n-1, s/3.0)
       left 90
       fractal(n-1, s/3.0)
    end
end

USAGE = <<MSG
Usage: turtle_viewer.rb quiz_125_fractal.rb [level]
\twhere level is a single digit integer
\trecommend level be less than 6
MSG

n = case ARGV[0]
     when nil then 3 # show level 3 if no argument given
     when /^\d$/ then ARGV[0].to_i
     else
        puts USAGE
        exit
     end
go [-190, -100]
right 90
pen_down
fractal(n, 380)
</code>

The turtle_viewer.rb script, also developed for Quiz 104 can run the above turtle program and produce the desired graphic output. But there is a problem. The turtle program wants a command line parameter and turtle_viewer.rb, without modification, doesn't make provision for this. Not to worry -- changing one line in turtle_viewer.rb fixes it up.

<code>
#! /usr/bin/env ruby -w
# Created by Morton Goldberg on November 02, 2006.
# Modified on November 17, 2006
# Modified on May 26, 2007
# turtle_viewer.rb

ROOT_DIR = File.dirname(__FILE__)
$LOAD_PATH << File.join(ROOT_DIR, "lib")

require 'tk'
require 'turtle_view'
require 'turtle'

# A simple Ruby/Tk script for viewing turtle graphics.

···

#
# If a file path is supplied as the first command line argument, the
# file is taken as the source for the turtle program to be run. If no
# argument is given, a default turtle program (CIRCLE_DESIGN -- see
# below) is run.

class TurtleViewer
    def initialize(code)
       @code = code
       # Create and lay out the viewer. Its only widget is a canvas.
       root = TkRoot.new {
          bg "DodgerBlue2"
          title "Turtle Graphics Viewer"
          }
       @canvas = TkCanvas.new(root) {
          relief :solid
          borderwidth 1
       }
       @canvas.pack(:fill=>:both, :expand=>true, :padx=>20, :pady=>20)
       # Run turtle commands when the canvas is mapped by Tk.
       @canvas.bind('Map') { run_code }
       # Set the window geometry; i.e., size and placement.
       win_w, win_h = 440, 440
       win_x = (root.winfo_screenwidth - win_w) / 2
       root.geometry("#{win_w}x#{win_h}+#{win_x}+50")
       root.resizable(false, false)
       # Make Cmnd+Q work as expected on Moc OS X.
       root.bind('Command-q') { Tk.root.destroy }
    end

    def run_code
       turtle = Turtle.new
       view = TurtleView.new(turtle, @canvas)
       view.handle_map_event(TkWinfo.width(@canvas),
                             TkWinfo.height(@canvas))
       turtle.run(@code)
       view.draw
    end
end

# Commands to be run if no command line argument is given.
CIRCLE_DESIGN = <<CODE
def circle
    pd; 90.times { fd 6; rt 4 }; pu
end
18.times { circle; rt 20 }
CODE

if ARGV.size > 0
    code = open(ARGV.shift) { |f| f.read } # **** modified ****
else
    code = CIRCLE_DESIGN
end
TurtleViewer.new(code)
Tk.mainloop
</code>

Regards, Morton

Here's my solution. It's a combination of iteration and recursion.
Drawing the fractal is itself iterative, but the rotate method is
recursive. The -f option writes output to a file rather than the
console. My program displays the output in simple ASCII and I did not
use the turtle graphics approach.

# file: fractal.rb
# author: Drew Olson

# Fractal class holds our fractal representation
class Fractal
  def initialize level
    raise ArgumentError if level<0
    @fractal = build_fractal level
  end

  # to print the fractal, we flip the array (to print with base of the
  # triangle on the bottom), format the fields so we get a space for
nils
  # in the array, and join all the array rows together with new lines
  def to_s
    @fractal.reverse.map do |row|
      row.map{|char| "%1s" % char}.join("")
    end.join("\n")
  end

  private

  # the height of the fractal can be calculated using the sum
  # below
  def get_height level
    (1..level).inject(1){|sum,i| sum+3**(i-1)}
  end

  # this method tells us which direction to turn after drawing
  # character i. if i%5 is 0..3, we make our standard move, dictated
  # by the shape of the fractal, every time. if i%5 is 4, we make a
  # move that is resursively defined by the fractal, hence we recal
  # get_dir after dividing i by 5.
  # cc - counter-clockwise
  # c - clockwise
  def get_dir i
    if i%5 == 4
      get_dir(i/5)
    elsif i%5 == 0 || i%5 == 3
      :cc
    elsif i%5 == 1 || i%5 == 2
      :c
    end
  end

  # here we define the direction that results when rotating
  # from the current direction either clockwise or counter-clockwise
  def rotate heading,dir
    if heading == :n
      dir == :cc ? :w : :e
    elsif heading == :s
      dir == :cc ? :e : :w
    elsif heading == :e
      dir == :cc ? :n : :s
    elsif heading == :w
      dir == :cc ? :s : :n
    end
  end

  # builds the fractal, given a level
  def build_fractal level
    # initialize heading and coordinates
    heading = :e
    x,y = 0,0

    # build a 2D array initialized to the correct height. i represents
the
    # index of the current character we are drawing.
    (0...5**level).inject(Array.new(get_height(level)){}) do

fractal,i|

      # store character in array based on heading, then update
      # x y coordinates
      if heading == :n
        fractal[y] = "|"
        x += 1
        y += 1
      elsif heading == :s
        y -= 1
        fractal[y] = "|"
        x += 1
      elsif heading == :e
        fractal[y] = "_"
        x += 1
      elsif heading == :w
        x -= 2
        fractal[y] = "_"
        x -= 1
      end
      # determine new heading
      heading = rotate(heading,get_dir(i))
      fractal
    end
  end
end

# handles IO. the -f flag takes a file name and writes the
# output to a file. if the flag is excluded, the output is
# printed to the console
# Usage:
# ruby fractal.rb 3 -> prints level 3 fractal to the console
# ruby fractal.rb 6 -f level6.txt -> prints level 6 fractal to file
if __FILE__ == $0
  if ARGV[1] == "-f"
    file_name = ARGV[2]
    File.open(file_name,"w") do |out|
      Fractal.new(ARGV[0].to_i).to_s.each do |line|
        out << line
      end
    end
  else
    puts Fractal.new(ARGV[0].to_i)
  end
end

···

--
Posted via http://www.ruby-forum.com/\.

I've gone for short but un-ruby-esque solution. Outputs an image in .pbm
format to stdout.

Regards,
Raf

$ cat rq125_fractals_rafc.rb
#!/usr/bin/ruby -w

# 0: North
# 1: East
# 2: South
# 3: West

#Transformation: the sequence of segments that a
#segment going North is transformed into:
t = [ 0, 3, 0, 1, 0]

#Starting situation: one segment going East:
f = [ 1]

#Build the fractal:
ARGV[0].to_i.times{ f = f.map{ |g| t.map{ |u| ( g + u) % 4}}.flatten}

#Draw the result:

dx = [ 0, 1, 0, -1]
dy = [ 1, 0, -1, 0]

#Determine dimension of drawing:
x = 0
y = 0
minx = 0
miny = 0
maxx = 0
maxy = 0
f.each do |g|
  x += dx[g]
  y += dy[g]
  minx = [x, minx].min
  miny = [y, miny].min
  maxx = [x, maxx].max
  maxy = [y, maxy].max
end

dimx = (maxx - minx) * 2 + 3
dimy = (maxy - miny) * 2 + 3

drawing = Array.new( dimy) { Array.new( dimx, 0)}

#Draw:
x = -minx * 2 + 1
y = -miny * 2 + 1

drawing[y][x] = 1

f.each{ |g|
  2.times {
    x += dx[g]
    y += dy[g]
    drawing[y][x] = 1
  }
}

#Output PBM:
puts 'P1'
puts "#{dimx} #{dimy}"
drawing.reverse_each { |row| puts row.join( ' ')}
__END__

$ #Usage:
$ ./rq125_fractals_rafc.rb 5 > f5.pbm

My solution is pastied and attached:

http://pastie.caboo.se/65680

I opted to render to the browser using the Safari/Firefox canvas object.
IE folks are out of luck as I didn't bother to include the javascript
canvas implementation.

- donald

# Ruby Quiz 125
# Donald Ball
# version 1.0

DIRECTIONS = [:east, :south, :west, :north]

def instructions(level=1, list=[:forward])
  if level == 1
    list
  else
    instructions(level-1, list.map do |item|
      case item
        when :forward
          [:forward, :left, :forward, :right, :forward, :right,
:forward, :left, :forward]
        else item
      end
    end.flatten)
  end
end

def plot(instructions)
  p = [0, 0]
  points = [p]
  direction = :east
  instructions.each do |item|
    case item
      when :forward
        p = p.dup
        case direction
          when :east: p[0] += 1
          when :south: p[1] -= 1
          when :west: p[0] -= 1
          when :north: p[1] += 1
        end
        points << p
      when :left: direction = DIRECTIONS[(DIRECTIONS.index(direction) -
1) % 4]
      when :right: direction = DIRECTIONS[(DIRECTIONS.index(direction) +
1) % 4]
    end
  end
  points
end

def to_html(points, width=600, height=400)
  length = [width/points.map {|p| p[0]}.max, height/points.map {|p|
p[1]}.max].min
  points.map! {|p| p.map {|x| x*length}}
  s = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
  s << "\n<html><head>"
  s << "<script type='text/javascript'>\n"
  s << "<!--\n"
  s << "function draw() {\n"
  s << "var context =
document.getElementById('canvas').getContext('2d');\n"
  s << "context.lineWidth = 1;\n"
  s << "context.strokeStyle = '#ff0000';\n"
  s << "context.beginPath();\n"
  p = points.shift
  s << "context.moveTo(#{p[0]},#{p[1]});\n"
  points.each {|p| s << "context.lineTo(#{p[0]}, #{p[1]});\n" }
  s << "context.stroke();\n"
  s << "}\n"
  s << "//-->\n"
  s << "</script>"
  s << "<body onload='draw()'>"
  s << "<canvas id='canvas' width='#{width}'
height='#{height}'></canvas>"
  s << "</body></html>"
  s
end

if $0 == __FILE__
  puts to_html(plot(instructions(ARGV[0].to_i))) if ARGV.length == 1
end

Here's my solution... It's the bare minimum of what I wanted to do.
I've been wanting to do some L-System type stuff for a while, so I
focused a bit on that. There's lots more I want to put into it, like
fractional levels, and support for contextual grammars (this is
context-free).

The design was inspired by Dennis Ranke's second solution to the dice
rolling quiz (#61). I had intended to reuse parts of the turtle
graphics quiz, but seeing as how I've also got "learn SVG" on my to-do
list, I did some quick and dirty SVG output. (Mozilla should be able
to open the file.)

class LSystem

  def initialize(&block)
    @rules = {}
    instance_eval(&block)
  end

  def rule(var)
    raise "Rule for #{var} must be unique!" if @rules.include?(var.to_sym)
    @rules[var.to_sym] = yield.map { |x| x.to_sym }
  end

  def start(var)
    @start = var.to_sym
  end

  def evaluate(n)
    product = [@start]
    n.to_i.times do |i|
      product.map! do |s|
        @rules[s.to_sym] || s.to_sym
      end.flatten!
    end

    product.each do |p|
      send(p)
    end
  end

end

koch = LSystem.new do
  start :F
  rule(:F) { %w(F + F - F - F + F) }

  def F
    nx, ny = @x + @dx, @y + @dy
    puts <<-LINE
<line x1="#{@x}" y1="#{@y}" x2="#{nx}" y2="#{ny}" stroke="black" />
    LINE
    @x, @y = nx, ny
  end

  def +
    @dx, @dy = -@dy, @dx
  end

  def -
    @dx, @dy = @dy, -@dx
  end

  def evaluate(n)
    raise "N must be non-negative" if n < 0
    @x, @y = 0, 0
    @dx, @dy = 900 / (3 ** n), 0

    puts <<-HEADER
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
  width="900px" height="450px" viewBox="0 0 900 450" >
    HEADER

    super
    
    puts <<-FOOTER
</svg>
    FOOTER
  end
end

koch.evaluate(ARGV[0].to_i)

ACK!
"which is what were used to now days."
Sorry
Please read that as
"which is what we're used to now days."

I wrote pretty much the same thing in my summary to this previous quiz:

http://www.rubyquiz.com/quiz117.html

I used a more compact PPM format though.

James Edward Gray II

···

On May 25, 2007, at 8:44 AM, Kyle Schmitt wrote:

If you want to create an image to view instead of on the console,
RMagic/RScience may be convenient, but never forget how plain old easy
it is to make a PPM image :slight_smile:

You guys should know by now that I'm pretty light on rules. Officially, the "intent of this quiz" is to learn something and/or have some fun. :wink:

I say, if you can solve it simply with turtle graphics, show us. I doubt everyone will try that so it certainly has a unique edge.

James Edward Gray II

···

On May 25, 2007, at 6:53 PM, Morton Goldberg wrote:

I'm confused about the intent of this quiz. Is the main focus writing code that will re-create the ASCII art shown above, or is it just to draw the required fractal by any means of the solver's choice (for example, using the turtle geometry classes developed in Quiz 104)? If the latter is permitted, a simple, recursive turtle program will do the job.

Wrote it as binary?

Yea I guess that is quite a bit more compact :slight_smile:

--Kyle

James, given the way the quiz is currently formulated, I do realize _you_ would never dump on a turtle graphics solution. However, I wonder whether or not the quiz author would welcome such a solution. I suspect he thinks providing ASCII output is an integral part of his quiz. I seek clarification because I think the quiz author's point of view should be respected.

Regards, Morton

···

On May 25, 2007, at 11:58 PM, James Edward Gray II wrote:

On May 25, 2007, at 6:53 PM, Morton Goldberg wrote:

I'm confused about the intent of this quiz. Is the main focus writing code that will re-create the ASCII art shown above, or is it just to draw the required fractal by any means of the solver's choice (for example, using the turtle geometry classes developed in Quiz 104)? If the latter is permitted, a simple, recursive turtle program will do the job.

You guys should know by now that I'm pretty light on rules. Officially, the "intent of this quiz" is to learn something and/or have some fun. :wink:

I say, if you can solve it simply with turtle graphics, show us. I doubt everyone will try that so it certainly has a unique edge.

Well, he did invite graphic solutions in the quiz description...

James Edward Gray II

···

On May 26, 2007, at 1:02 PM, Morton Goldberg wrote:

On May 25, 2007, at 11:58 PM, James Edward Gray II wrote:

On May 25, 2007, at 6:53 PM, Morton Goldberg wrote:

I'm confused about the intent of this quiz. Is the main focus writing code that will re-create the ASCII art shown above, or is it just to draw the required fractal by any means of the solver's choice (for example, using the turtle geometry classes developed in Quiz 104)? If the latter is permitted, a simple, recursive turtle program will do the job.

You guys should know by now that I'm pretty light on rules. Officially, the "intent of this quiz" is to learn something and/or have some fun. :wink:

I say, if you can solve it simply with turtle graphics, show us. I doubt everyone will try that so it certainly has a unique edge.

James, given the way the quiz is currently formulated, I do realize _you_ would never dump on a turtle graphics solution. However, I wonder whether or not the quiz author would welcome such a solution. I suspect he thinks providing ASCII output is an integral part of his quiz. I seek clarification because I think the quiz author's point of view should be respected.

Morton Goldberg wrote:

Officially, the "intent of this quiz" is to learn something and/or
have some fun. :wink:

I say, if you can solve it simply with turtle graphics, show us. I
doubt everyone will try that so it certainly has a unique edge.

James, given the way the quiz is currently formulated, I do realize
_you_ would never dump on a turtle graphics solution. However, I
wonder whether or not the quiz author would welcome such a solution.
I suspect he thinks providing ASCII output is an integral part of his
quiz. I seek clarification because I think the quiz author's point of
view should be respected.

Regards, Morton

Morton -

I'm with James here. The spirit of the quiz is the display the output in
any way you see fit. I wrote the quiz based on ASCII output as it would
allow those who didn't want to use any external libraries or tools the
opportunity to participate. I wrote my solution based on the ASCII, but
I would be excited to see you solve it using the turtle solution. In
short, I say use whatever tool you feel provides the best solution!

- Drew

···

On May 25, 2007, at 11:58 PM, James Edward Gray II wrote:

--
Posted via http://www.ruby-forum.com/\.

I just re-read the quiz description and the sentence

    Feel free to use the console for output or get fancy with RMagick or
    something similar.

got my attention this time around. My eyes must have slipped over it the first time. Maybe because RMagick didn't mean anything to me. But, given your comment and after a Google search on RMagick, I agree that a turtle graphics solution would probably be welcomed by the quiz author. Mine is up and running -- took less than ten minutes -- and I will submit it after the non-spoiler period expires. I attach its level 4 output.

Regards, Morton

quiz_125_fractal.pdf (9.29 KB)

···

On May 26, 2007, at 3:02 PM, James Edward Gray II wrote:

Well, he did invite graphic solutions in the quiz description...