On 04/12/2006, at 8:28 PM, Edwin Fine wrote:
> Here's my solution.
Ok, a couple of comments.
First, your home method doesn't raise the pen as it should.
Second, all that flipping between turtle space angles and normal
angles is unnecessary. Just swap the x and y axes when doing the trig
and you'll get the right result.
Third, Ruby has an atan2 method that does most of what you do in your
toward method.
Fourth, your normalize_degrees method is overkill. Try '-10 % 360' in
irb.
Here's my solution:
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 unless is_point?(coords)
@xy = coords
end
# Set the turtle's heading to <degrees>.
def heading=(degrees)
raise ArgumentError unless degrees.is_a?(Numeric)
@heading = degrees % 360
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_is_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_is_down = true
@track << [@xy]
end
# Is the pen up?
def pen_up?
!@pen_is_down
end
# Is the pen down?
def pen_down?
@pen_is_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
@heading = 0.0
@xy = [0.0, 0.0]
@pen_is_down = false
end
# Homes the turtle and empties out it's track.
def clear
@track = []
home
end
# Turn right through the angle <degrees>.
def right(degrees)
raise ArgumentError unless degrees.is_a?(Numeric)
@heading += degrees
@heading %= 360
end
# Turn left through the angle <degrees>.
def left(degrees)
right(-degrees)
end
# Move forward by <steps> turtle steps.
def forward(steps)
raise ArgumentError unless steps.is_a?(Numeric)
@xy = [@xy.first + sin(@heading * DEG) * steps, @xy.last + cos
(@heading * DEG) * steps]
@track.last << @xy if @pen_is_down
end
# Move backward by <steps> turtle steps.
def back(steps)
forward(-steps)
end
# Move to the given point.
def go(pt)
raise ArgumentError unless is_point?(pt)
@xy = pt
@track.last << @xy if @pen_is_down
end
# Turn to face the given point.
def toward(pt)
raise ArgumentError unless is_point?(pt)
@heading = (atan2(pt.first - @xy.first, pt.last - @xy.last) /
DEG) % 360
end
# Return the distance between the turtle and the given point.
def distance(pt)
raise ArgumentError unless is_point?(pt)
return sqrt((pt.first - @xy.first) ** 2 + (pt.last - @xy.last)
** 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 is_point?(pt)
pt.is_a?(Array) and pt.length == 2 and pt.first.is_a?(Numeric)
and pt.last.is_a?(Numeric)
end
end