|
|
 |
 |
 |
 |
Ruby Programming Language
|
 |
 |
 |
 |
 |
 |
 |
 |
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 :) 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.
ACK! "which is what were used to now days." Sorry Please read that as "which is what we're used to now days."
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 :)
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
Wrote it as binary? Yea I guess that is quite a bit more compact :) --Kyle
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 > _| |_ > _|_ _|_ > _| |_|_ _ _ _|_| |_ > _| |_|_|_| |_|_|_| |_ > _|_ _|_|_ _|_|_ _|_ > _| |_| |_| |_| |_| |_| |_| |_
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 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. ;) 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 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. ;) > 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
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. ;) >> 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.
Well, he did invite graphic solutions in the quiz description... James Edward Gray II
Morton Goldberg wrote: > On May 25, 2007, at 11:58 PM, James Edward Gray II wrote: >> Officially, the "intent of this quiz" is to learn something and/or >> have some fun. ;) >> 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 -- Posted via http://www.ruby-forum.com/.
[
multipart_mixed_part ]
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_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... http://www.jessemerriman.com/images/ruby_quiz/125_fractals/fractal_F_... -- Jesse Merriman jessemerri@warpmail.net http://www.jessemerriman.com/
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 > _|_ _|_ > _| |_| |_| |_
#!/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. # # +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[x][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 :-) -- No virus found in this outgoing message. Checked by 'grep -i virus $MESSAGE' Trust me.
|
|
application_pgp-signature_part
1K
Download
|
On Fri, 25 May 2007 22:18:32 +0900, Ruby Quiz wrote: > 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 > _| |_ > _|_ _|_ > _| |_|_ _ _ _|_| |_ > _| |_|_|_| |_|_|_| |_ > _|_ _|_|_ _|_|_ _|_ > _| |_| |_| |_| |_| |_| |_| |_
#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/
On Fri, 25 May 2007 22:18:32 +0900, Ruby Quiz wrote: > 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 > _| |_ > _|_ _|_ > _| |_|_ _ _ _|_| |_ > _| |_|_|_| |_|_|_| |_ > _|_ _|_|_ _|_|_ _|_ > _| |_| |_| |_| |_| |_| |_| |_
#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
Hi, here is my solution. The fractal-part is straight forward, the ascii-art was harder. thanks, Philip -------------------------------- #!/usr/bin/ruby -w class Fractal def initialize n @steps={:up =>[:up, :left, :up, :right, :up], :down=>[:down, :right, :down, :left, :down], :left=>[:left, :down, :left, :up, :left], :right=>[:right, :up, :right, :down, :right] } @n=n @moves=(0..n).to_a.inject([]) {|result, ignore| one_step(result) } end def to_s x=0 y=0 width = 3 ** @n height= (1+width)/2 screen= (1.. height).collect { " " *(2* width-1) } @moves.each {|item| case item when :up y+=1 (screen[y-1])[x-1]="|" when :down y-=1 (screen[y])[x-1]="|" when :left x-=2 (screen[y])[x]="_" when :right x+=2 (screen[y])[x-2]="_" end } screen.reverse.join("\n") end private def one_step x x.empty? ? [:right] : (x.collect {|item| @steps[item] }).flatten end end n=ARGV[0].to_i if (ARGV.length < 1 || n<0) puts "Usage: quizz_125.rb level" puts "-> level: non-negative integer" else puts Fractal.new(n) end
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] = "|" x += 1 y += 1 elsif heading == :s y -= 1 fractal[y][x] = "|" x += 1 elsif heading == :e fractal[y][x] = "_" x += 1 elsif heading == :w x -= 2 fractal[y][x] = "_" 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/.
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)
#---------------------------------------------------------------------# # # # Program : Fractals (Ruby Quiz #125) # # Author : David Tran # # Date : 2007-05-30 # # Blog : http://davidtran.doublegifts.com/blog/?p=48 # # Reference : http://mathworld.wolfram.com/PerpendicularVector.html # # Note : Using vector calculation to compute each level's # # points. The first level line can be in any direction. # # # #---------------------------------------------------------------------# require 'enumerator' require 'RMagick' LEVEL_0 = [[0, 0], [350, 200]] def next_level(polylines) polylines.enum_cons(2).inject([polylines.first]) do |array, (p1, p2)| x1, y1 = p1 x2, y2 = p2 a = (x2 - x1) / 3.0 b = (y2 - y1) / 3.0 array << [x1+a, y1+b] << [x1+a-b, y1+b+a] << [x1+2*a-b, y1+2*b+a] << [x1+2*a, y1+2*b] << p2 end end exit unless __FILE__ == $0 imageList = Magick::ImageList.new level = LEVEL_0 (ARGV[0].to_i + 1).times do |i| level = next_level(level) if i > 0 image = Magick::Image.new(400, 300) image.delay = 100 draw = Magick::Draw.new draw.fill_opacity(0) draw.stroke('black') draw.polyline(*level) draw.text(300,100,"level #{i}") draw.draw(image) imageList << image end imageList.write(File.basename($0) + ".gif")
I lied to Morton Goldberg. I told him I thought a turtle graphics solution would be a unique approach not many people would try. Boy was I wrong! That turns out to be the easiest way to attack this problem and the solvers knew that a lot better than I did. The solutions are very diverse and I had a hard time picking what to show. Some solutions allowed you to customize the fractal drawn. Others offered various forms of output, including some pretty pictures. Jesse Merriman's solution did it all and I do recommend having a peek at that code. For this summary though, I'm going to show Donald Ball's solution. I think it captured the technique most people used well and it also included a unique form of output. Let's take a look: 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 # ... This method sums up the shortcuts pretty much everyone used. First, we have turtle graphics style drawing. If we assume a turtle starts facing east, the Array in the middle of this method contains the instructions to draw level one of the fractal. It's much easier to work with a format like this than it is to edit a large String or Array of paths. The other shortcut was a simplification of the drawing pattern. It turns out that just replacing all occurrences of the level zero pattern with the level one pattern is all it takes to transform any level of the pattern into the next level. Knowing that, this method is a recursive implementation of those shortcuts. It begins with the level zero pattern and for each level it performs the replacement I mentioned earlier. The end result will be a long path for drawing the entire target level. The next step is to convert those directions into and collection of points that could be plotted: # ... DIRECTIONS = [:east, :south, :west, :north] 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 # ... This method generates a collection of points from the path of :forward, :left, and :right instructions. It works by tracking the direction the turtle is currently facing and moving by incrementing the points as indicated for that direction. Turns simply change the direction. Now, it's not so easy to flatten the coordinates into an ASCII art string and we really want to see these fractals as an image anyway, so Donald opted for an unusual means of output. Donald's program builds a web page that can draw the fractal (as long as it is displayed in Safari or Firefox). Here's the code: # ... 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" ' s << '"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 # ... The work here is pretty simple. First the code finds a good line width for the fractal by measuring how much space the canvas has for width and height. All the points are then scaled by that length. This is done with simple multiplication since the scale was previously one. The output is some HTML preamble followed by a JavaScript section that does the real work. The prepares a drawing context and then literally just plays connect the dots with the points. There's a little more HTML to close things up and invoke the drawing routine and, presto, we have a self drawing web page. As written, this code draws fractals upside down (in comparison to the quiz examples). If you want to flip them over, just change to two #{p[1]} interpolations in the JavaScript to #{height - p[1]}. We're just short the code to kick-off the process: # ... if $0 == __FILE__ puts to_html(plot(instructions(ARGV[0].to_i))) if ARGV.length == 1 end As usual, this code is trivial. Generate instructions() for the requested lever, hand those off to plot() to get the coordinates, and filter those through to_html() to get a printable web page. My thanks to all who showed me just how powerful turtle graphics can be. I should have never doubted. Tomorrow we will give a little time to a problem that has been making the rounds in the hope of making ourselves more employable...
|
 |
 |
 |
 |
|