From: Dema Date: 2006-12-06T11:00:08+09:00 Subject: Re: Turtle Graphics (#104) Thanks for pointing that out. I don't even know how all the sample drawings were right with that huge bug in the code. Here is the corrected version that passes your updated tests (sorry, it took me so long to reply). Seeing the other solutions I feel that mine is probably not the best, but perhaps the most concise one. I tried to be very "economic" on the line count. (Please James, could you update my solution link on the rubyquiz site?) 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) set_heading 90.0 - atan2(pt[1] - @xy[1], pt[0] - @xy[0]) / 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 Morton Goldberg wrote: > On Dec 3, 2006, at 8:30 PM, Dema wrote: > > > Here is my straight-to-the-point answer: > > Your solution passes all the unit tests I supplied and is certainly > good enough to reproduce all the sample designs. So you have good > reason to think it's completely correct. However, one of the optional > methods has a problem. > > > # Turn to face the given point. > > def toward(pt) > > @heading = atan(pt[0].to_f / pt[1].to_f) / DEG > > end > > This won't work in all four quadrants. > > I apologize for not providing tests good enough to detect the > problem. Here is one that will test all four quadrants. > > > # Test go, toward, and distance. > # Verify heading measures angles clockwise from north. > def test_coord_cmnds > nne = [100, 173] > @turtle.go nne > x, y = @turtle.xy > assert_equal(nne, [x.round, y.round]) > @turtle.home > @turtle.run { pd; face nne; fd 200 } > assert_equal(30, @turtle.heading.round) > assert_equal([[[0, 0], nne]], snap(@turtle.track)) > sse = [100, -173] > @turtle.home > @turtle.run { face sse; fd 200 } > assert_equal(150, @turtle.heading.round) > ssw = [-100, -173] > @turtle.home > @turtle.run { face ssw; fd 200 } > assert_equal(210, @turtle.heading.round) > nnw = [-100, 173] > @turtle.home > @turtle.run { face nnw; fd 200 } > assert_equal(330, @turtle.heading.round) > @turtle.home > assert_equal(500, @turtle.dist([400, 300]).round) > end > > > Regards, Morton