From: Dema Date: 2006-12-04T10:30:05+09:00 Subject: Re: Turtle Graphics (#104) Here is my straight-to-the-point answer: class Turtle include Math # turtles understand math methods DEG = Math::PI / 180.0 attr_accessor :track alias run instance_eval def initialize clear end attr_reader :xy, :heading # Place the turtle at [x, y]. The turtle does not draw when it changes # position. def xy=(coords) raise ArgumentError if !coords.is_a?(Array) || coords.size != 2 || coords.any? { |c| !c.is_a?(Numeric) } @xy = coords end # Set the turtle's heading to . def heading=(degrees) raise ArgumentError if !degrees.is_a?(Numeric) set_heading(degrees) end # Raise the turtle's pen. If the pen is up, the turtle will not draw; # i.e., it will cease to lay a track until a pen_down command is given. def pen_up @pen_down = false end # Lower the turtle's pen. If the pen is down, the turtle will draw; # i.e., it will lay a track until a pen_up command is given. def pen_down @pen_down = true end # Is the pen up? def pen_up? !@pen_down end # Is the pen down? def pen_down? @pen_down end # Places the turtle at the origin, facing north, with its pen up. # The turtle does not draw when it goes home. def home pen_up @xy = [0,0] @heading = 0 end # Homes the turtle and empties out it's track. def clear home @track = [] end # Turn right through the angle . def right(degrees) set_heading(@heading + degrees) end # Turn left through the angle . def left(degrees) set_heading(@heading - degrees) end # Move forward by turtle steps. def forward(steps) dx, dy = calc_delta(steps) go [ @xy[0] + dx, @xy[1] + dy ] end # Move backward by turtle steps. def back(steps) dx, dy = calc_delta(steps) go [ @xy[0] - dx, @xy[1] - dy ] end # Move to the given point. def go(pt) track << [ @xy, pt ] if pen_down? @xy = pt end # Turn to face the given point. def toward(pt) @heading = atan(pt[0].to_f / pt[1].to_f) / DEG end # Return the distance between the turtle and the given point. def distance(pt) sqrt((@xy[0] - pt[0]) ** 2 + (@xy[1] - pt[1]) ** 2) end # Traditional abbreviations for turtle commands. alias fd forward alias bk back alias rt right alias lt left alias pu pen_up alias pd pen_down alias pu? pen_up? alias pd? pen_down? alias set_h heading= alias set_xy xy= alias face toward alias dist distance private def set_heading(degrees) @heading = degrees % 360 end def calc_delta(steps) [ sin(heading * DEG) * steps, cos(heading * DEG) * steps ] end end 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 Morton Goldberg > > [Editor's Note: You can download the files for this quiz at: > > http://rubyquiz.com/turtle.zip > > --JEG2] > > Turtle Graphics > =============== > > Turtle graphics is a form of computer graphics based on the ideas of turtle > geometry, a formulation of local (coordinate-free) geometry. As a brief > introduction to turtle graphics, I quote from [1]: > > Imagine that you have control of a little creature called a turtle > that exists in a mathematical plane or, better yet, on a computer > display screen. The turtle can respond to a few simple commands: > FORWARD moves the turtle in the direction it is facing some > number of units. RIGHT rotates it clockwise in its place some > number of degrees. BACK and LEFT cause the opposite movements. ... > The turtle can leave a trace of the places it has been: [its > movements] can cause lines to appear on the screen. This is > controlled by the commands PENUP and PENDOWN. When the pen is > down, the turtle draws lines. > > For example, the turtle commands to draw a square, 100 units on a side, can be > written (in a Ruby-ized form) as: > > pen_down > 4.times { forward 100; right 90 } > > For more information, see [2] and [3]. > > This quiz is a bit different from most. If the usual Ruby quiz can be likened to > an essay exam, this one is a fill-in-the-blanks test. I'm supplying you with a > complete turtle graphics package, except -- to give you something to do -- I've > removed the method bodies from the key file, lib/turtle.rb. Your job is to > repair the damage I've done and make the package work again. > > Turtle Commands > =============== > > There are quite a few turtle commands, but that doesn't mean you have to write a > lot of code to solve this quiz. Most of the commands can be implemented in a > couple of lines. It took me a lot longer to write a description of the commands > than it did for me to implement and test all of them. > > I use the following format to describe turtle commands: > > long_name | short_name > description ... > Example: ... > > All turtle commands take either one argument or none, and not all turtle > commands have both a long name and a short name. > > Required Commands > ----------------- > > These commands are required in the sense that they are needed to reproduce the > sample designs. Actually, you could get away without implementing 'back' and > 'left', but implementing them is far easier than trying to write turtle code > without them. > > pen_up | pu > Raises the turtle's pen. The turtle doesn't draw (lay down a visible > track) when its pen is up. > > pen_down | pd > Lowers the turtle's pen. The turtle draws (lays down a visible track) > when its pen is down. > > forward | fd > Moves the turtle forwards in the direction it is facing. > Example: forward(100) advances the turtle by 100 steps. > > back | bk > Moves the turtle backwards along its line of motion. > back == forward - > Example: back(100) backs up the turtle by 100 steps. > > right | rt > Turns the turtle clockwise by degrees. > Example: right(90) turns the turtle clockwise by a right angle. > > left | lt > Turns the turtle counterclockwise by degrees. > left == right - > Example: left(45) turns the turtle counterclockwise by 45 degrees. > > Traditional Commands > -------------------- > > These commands are not needed to reproduce any of the sample designs, but they > are found in all implementations of turtle graphics that I know of. > > home > Places the turtle at the origin, facing north, with its pen up. The > turtle does not draw when it goes home. > > clear > Homes the turtle and empties out it's track. Sending a turtle a clear > message essentially reinitializes it. > > xy > Reports the turtle's location. > Example: Suppose the turtle is 10 turtle steps north and 15 turtle steps > west of the origin, then xy will return [-15.0, 10.0]. > > set_xy | xy= > Places the turtle at . The turtle does not draw when this command > is executed, not even if its pen is down. Returns . > Example: Suppose the turtle is at [10.0, 20.0], then self.xy = [50, 80] > moves the turtle to [50.0, 80.0], but no line will drawn between the [10, > 20] and [50, 80]. > > heading > Reports the direction in which the turtle is facing. Heading is measured > in degrees, clockwise from north. > Example: Suppose the turtle is at the origin facing the point [100, 200], > then heading will return 26.565 (approximately). > > heading= | set_h > Sets the turtle's heading to . should be given in degrees, > measured clockwise from north. Returns . > Example: After self.heading = 135 (or set_h(135) which is easier to > write), the turtle will be facing southeast. > > pen_up? | pu? > Reports true if the turtle's pen is up and false otherwise. > > pen_down? | pd? > Reports true if the turtle's pen is down and false otherwise. > > Optional Commands > ----------------- > > These commands are only found in some implementations of turtle graphics. When > they are implemented, they make the turtle capable of doing global (coordinate) > geometry in addition to local (coordinate-free) geometry. > > I used one of these commands, go, to draw the mandala design (see > designs/mandala.tiff and samples/mandala.rb). If you choose not to implement the > optional commands, you might try writing a turtle program for drawing the > mandala design without using go. But, believe me, it is much easier to implement > go than to write such a program. > > go > Moves the turtle to . > Example: Suppose the turtle is home (at the origin facing north). After > go([100, 200]), the turtle will be located at [100.0, 200.0] but will > still be facing north. If its pen was down, it will have drawn a line > from [0, 0] to [100, 200]. > > toward | face > Turns the turtle to face . > Example: Suppose the turtle is at the origin. After toward([100, 200]), > its heading will be 26.565 (approximately). > > distance | dist > Reports the distance between the turtle and . > Example: Suppose the turtle is at the origin, then distance([400, 300]) > will return 500.0 (approximately). > > Interfacing to the Turtle Graphics Viewer > ========================================= > > Implementing turtle graphics without being able to view what the turtle draws > isn't much fun, so I'm providing a simple turtle graphics viewer. To interface > with the viewer, turtle instances must respond to the message track by returning > an array which the viewer can use to generate a line drawing. > > The viewer expects the array returned by track to take the following form: > > track ::= [segment, segment, ...] # drawing data > segment ::= [point, point, ...] # points to be joined by line segments > point ::= [x, y] # pair of floats > > Example: [[[0.0, 0.0], [200.0, 200.0]], [[200.0, 0.0], [0.0, 200.0]]] > > This represents an X located in the upper-right quadrant of the viewer; i.e., > two line segments, one running from the center of the viewer up to its > upper-right corner and the other running from the center of the top edge down to > the center of the right edge. > > [Editor's Note: I added a script to dump your turtle graphics output to PPM > image files, for those that don't have TK up and running. It works identically > to Morton's turtle_viewer.rb, save that it writes output to a PPM image file in > the current directory. For example, to output the included tree image, use > `ruby turtle_ppm_writer.rb samples/tree.rb`. --JEG2] > > Unit Tests > ========== > > I'm including the unit tests which I developed to test turtle commands. For the > purposes of the quiz, you can ignore tests/turtle_view_test.rb. But I hope you > will find the other test suite, tests/turtle_test.rb, helpful. It tests every > one of the turtle commands described above as well as argument checking by the > commands. Don't hesitate to modify any of the unit tests to meet the needs of > your quiz solution. > > References > ========== > > [1] Abelson, H. & A. diSessa, "Turtle Geometry", MIT Press, 1981. > [2] Harvey, B., "Computer Science Logo Style", Chapter 10. > http://www.cs.berkeley.edu/~bh/pdf/v1ch10.pdf > [3] Wikipedia, http://en.wikipedia.org/wiki/LOGO_programming_language