From: David Tran Date: 2006-12-08T00:15:20+09:00 Subject: [Solution] [Quiz] Turtle Graphics (#104) My second solution. This solution pass the unit test. It tests all parameters also makes sure 0.0 <= @heading < 360.0 # Created by Morton Goldberg on November 02, 2006. # Modified on November 14, 2006 # turtle.rb # An implementation of Turtle Procedure Notation (TPN) as described in # H. Abelson & A. diSessa, "Turtle Geometry", MIT Press, 1981. # # Turtles navigate by traditional geographic coordinates: X-axis pointing # east, Y-axis pointing north, and angles measured clockwise from the # Y-axis (north) in degrees. 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) @xy = point(coords) end # Set the turtle's heading to . def heading=(degrees) @heading = degree(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 = 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 = true end # Is the pen up? def pen_up? !pen_down? end # Is the pen down? def pen_down? @pen end # Places the turtle at the origin, facing north, with its pen up. # The turtle does not draw when it goes home. def home @xy = point([0.0, 0.0]) @heading = 0.0 pen_up end # Homes the turtle and empties out it's track. def clear home @track = [] end # Turn right through the angle . def right(degrees) degrees = degree(degrees) @heading = degree(@heading + degrees) end # Turn left through the angle . def left(degrees) right(-degrees) end # Move forward by turtle steps. def forward(steps) raise ArgumentError unless steps.is_a?(Numeric) angle = (90 - @heading) * DEG x = @xy.x + steps * cos(angle) y = @xy.y + steps * sin(angle) go [x,y] end # Move backward by turtle steps. def back(steps) forward(-steps) end # Move to the given point. def go(pt) pt = point(pt) if pen_down? if !@track.empty? && @track.last.last == @xy @track.last << pt else @track << [@xy, pt] end end @xy = pt end # Turn to face the given point. def toward(pt) pt = point(pt) @heading = degree(90 - atan2(pt.y - @xy.y, pt.x - @xy.x) / DEG) end # Return the distance between the turtle and the given point. def distance(pt) pt = point(pt) sqrt((@xy.x - pt.x) ** 2 + (@xy.y - pt.y) ** 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 point(pt) raise ArgumentError unless pt.is_a?(Array) && pt.size == 2 && pt.all? {|e| e.is_a?(Numeric) } pt = pt.dup def pt.x; self[0]; end def pt.y; self[1]; end pt end def degree(degrees) raise ArgumentError unless degrees.is_a?(Numeric) degrees += 360.0 while degrees < 0.0 degrees -= 360.0 while degrees >= 360.0 degrees end end