# [Solution] [Quiz] Turtle Graphics (#104)

Discussion in 'Ruby' started by David Tran, Dec 7, 2006.

1. ### David TranGuest

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:I / 180.0

attr_accessor :track
alias run instance_eval

def initialize
clear
end

# 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 <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])
pen_up
end

# Homes the turtle and empties out it's track.
def clear
home
@track = []
end

# Turn right through the angle <degrees>.
def right(degrees)
degrees = degree(degrees)
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)
angle = (90 - @heading) * DEG
x = @xy.x + steps * cos(angle)
y = @xy.y + steps * sin(angle)
go [x,y]
end

# Move backward by <steps> 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_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

David Tran, Dec 7, 2006