[QUIZ] 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.

A bug in Turtle#toward has no effect on reproducing the sample designs because toward is not used in any of the sample turtle scripts. Turtle#toward is not part of core turtle graphics -- it is part of the optional non-local-geometry extensions found in some, but by no means all, turtle graphic packages. Implementing toward is can be considered an extra-credit exercise.

Morton Goldberg wrote:

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

IMO, Turtle#toward is the most difficult turtle command to get fully right. The version I posted as part of my solution is correct, but it's not the best that can be done. A better implementation would have been:

    # Turn to face the given point.
    def toward(pt)
       x2, y2 = pt
       must_be_number(x2, 'pt.x')
       must_be_number(y2, 'pt.y')
       x1, y1 = xy
       set_h(atan2(x2 - x1, y2 - y1) / DEG)
    end

But that's not what I had when I wrote the quiz -- this version incorporates an improvement I saw in Matthew Moss' solution.

Regards, Morton

···

On Dec 5, 2006, at 9:00 PM, Dema wrote:

Unfortunately, that kept me from trying very hard to fix it. I also worried they weren't too interested in the library since the example had notes about how it was busted.

I also had issues where I would draw lines in bounds, but it would access pixels outside the image area in anti-aliasing which caused exceptions to be tossed.

James Edward Gray II

···

On Dec 1, 2006, at 3:44 PM, Daniel Martin wrote:

That old line drawing algorithm I could just pull out of a book on my
shelf; what they're doing here is enough different that it's very
tough to disentangle.

To be fair, they don't do the compression in pure ruby - they call
Zlib, so the really computationally intensive bit isn't in ruby.

Thanks for the correction here.

Also, the fast line-drawing algorithm used for writing to that old
320x200 VGA mode wasn't what they're using here. Here, they're doing
*anti-aliased* lines, which no one would ever think of trying when you
only have 256 colors available and the pixels are going to be visibly
squares no matter what you do.

People were most definitely drawing anti-aliased lines in mode 13h:
http://freespace.virgin.net/hugo.elias/graphics/x_wuline.htm

Aesthetically sensible when, as you say, each pixel is a visible rectangle? no.

Still pretty fun though. hehe. :wink:

That old line drawing algorithm I could just pull out of a book on my
shelf; what they're doing here is enough different that it's very
tough to disentangle.

It's a bit of a shame too, this lib would otherwise be *perfect* for
doing cross-platform turtle graphics in Ruby.

Regards,
-Harold

···

On 12/2/06, Daniel Martin <martin@snowplow.org> wrote:

/usr/local/lib/ruby/site_ruby/1.8/tk.rb:1187: warning: instance variable
@encoding not initialized

Is it something I am doing wrong, or some other problem? I don't know
much about Tcl/Tk.

···

--
Posted via http://www.ruby-forum.com/.

People were most definitely drawing anti-aliased lines in mode 13h:
http://freespace.virgin.net/hugo.elias/graphics/x_wuline.htm

Aesthetically sensible when, as you say, each pixel is a visible rectangle? no.

Actually, it's exactly when you want anti-aliasing. If your pixels
were small enough, anti-aliasing would be unnecessary.

Of course, if your pixels are _very_ large, then yeah, I suppose it
won't help much. Maybe that's what you were getting at...

I'd look into this, but you aren't giving enough info for me to have a clue as to how to reproduce it.

OTOH, since it's only a warning, it probably OK to let it go for now unless you're seeing visible problems such as bad graphic output. I've seen a number of harmless warnings both from Ruby/Tk and Tk itself before.

Regards, Morton

···

On Dec 1, 2006, at 7:22 PM, Edwin Fine wrote:

/usr/local/lib/ruby/site_ruby/1.8/tk.rb:1187: warning: instance variable
@encoding not initialized

Is it something I am doing wrong, or some other problem? I don't know
much about Tcl/Tk.

Morton Goldberg wrote:

/usr/local/lib/ruby/site_ruby/1.8/tk.rb:1187: warning: instance
variable
@encoding not initialized

Is it something I am doing wrong, or some other problem? I don't know
much about Tcl/Tk.

I'd look into this, but you aren't giving enough info for me to have
a clue as to how to reproduce it.

OTOH, since it's only a warning, it probably OK to let it go for now
unless you're seeing visible problems such as bad graphic output.
I've seen a number of harmless warnings both from Ruby/Tk and Tk
itself before.

Regards, Morton

Sorry I didn't provide more information earlier; I kind of assumed that
other people would have got the warnings, too. Although the warnings are
in fact harmless, and the graphics appear to be just fine, it's a bit
annoying when you get 27,690 warnings (that's right: 27 thousand plus
warnings). These consisted of the following two warnings:

/usr/lib/ruby/1.8/tk.rb:2313: warning: redefine encoding=
/usr/lib/ruby/1.8/tk.rb:2316: warning: redefine encoding

followed by 27,688 repetitions of this warning:

/usr/lib/ruby/1.8/tk.rb:1187: warning: instance variable @encoding not
initialized

All I did was run this command line:

ruby turtle_viewer.rb samples/tree.rb

The warnings occur for any run of turtle_viewer.rb.

Here's my Ruby version:

ruby 1.8.4 (2005-12-24) [x86_64-linux]

I'm running this on Ubuntu Edgy x86-64. But I get the same warning when
I run on a 32-bit RHEL3 system using Ruby 1.8.5, too.

I can't post my code (yet) because of the 48-hour deadline, but I can
tell you that it does not have the word "encoding" anywhere in it :slight_smile:

I'll investigate further. I was just wondering if anyone else saw this.
Thanks.

···

On Dec 1, 2006, at 7:22 PM, Edwin Fine wrote:

--
Posted via http://www.ruby-forum.com/\.

Message-ID: <26e18cd66a293ea06332d951983f9db6@ruby-forum.com>

warnings). These consisted of the following two warnings:

/usr/lib/ruby/1.8/tk.rb:2313: warning: redefine encoding=
/usr/lib/ruby/1.8/tk.rb:2316: warning: redefine encoding

followed by 27,688 repetitions of this warning:

/usr/lib/ruby/1.8/tk.rb:1187: warning: instance variable @encoding not
initialized

Don't worry about those. Those are no problem.

Redefinition of TclTkLib.encoding and encoding= depends on
the difference between using 'tcltklib.so' only and with 'tk.rb'.

@encoding is used to check the encoding of a string.
It depends that the value of non-initialized instance varible is nil.
Of course, if check the existence of the variable first,
the warning will be removed.
But then, checking a string to pass to a Tcl/Tk interpreter needs
two steps (check and refer).
Such check is called very frequently. So, I ignored the warning.

···

From: Edwin Fine <efine145-nospam01@usa.net>
Subject: Re: tk.rb warning in Turtle Graphics (#104)
Date: Sat, 2 Dec 2006 17:03:13 +0900
--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

Hidetoshi NAGAI wrote:

···

From: Edwin Fine <efine145-nospam01@usa.net>
Subject: Re: tk.rb warning in Turtle Graphics (#104)
Date: Sat, 2 Dec 2006 17:03:13 +0900
Message-ID: <26e18cd66a293ea06332d951983f9db6@ruby-forum.com>

warnings). These consisted of the following two warnings:

/usr/lib/ruby/1.8/tk.rb:2313: warning: redefine encoding=
/usr/lib/ruby/1.8/tk.rb:2316: warning: redefine encoding

followed by 27,688 repetitions of this warning:

/usr/lib/ruby/1.8/tk.rb:1187: warning: instance variable @encoding not
initialized

Don't worry about those. Those are no problem.

Redefinition of TclTkLib.encoding and encoding= depends on
the difference between using 'tcltklib.so' only and with 'tk.rb'.

@encoding is used to check the encoding of a string.
It depends that the value of non-initialized instance varible is nil.
Of course, if check the existence of the variable first,
the warning will be removed.
But then, checking a string to pass to a Tcl/Tk interpreter needs
two steps (check and refer).
Such check is called very frequently. So, I ignored the warning.

-------------
Well, I found a bizarre and (to me) totally inexplicable way to get rid
of the warning. This is from the quiz turtle_view.rb file. Adding an
explicit return value (any value; I used nil) to the draw method gets
rid of the warning. WTF???

   # Transform the turtle's track into TkcLine objects and add them to
the
   # canvas' display list. This method expects a track to be an array of
   # the form
   # track ::= [segment, segment, ...]
   # segment ::= [point, point, ...]
   # point ::= [x, y]
   # where x and y are floats.
   def draw
     @turtle.track.each do |seqment|
        if seqment.size > 1
           pts = seqment.collect { |pt| transform(pt) }
           TkcLine.new(@canvas, pts)
        end
     end
     nil # Adding this (or any return value) stops that @encoding
warning
   end

I just dunno. Weird.

--
Posted via http://www.ruby-forum.com/\.

I'm just as mystified as you area as to why it works, but I'm glad you found a work-around that turns off all the warnings.

Regards, Morton

···

On Dec 2, 2006, at 4:28 AM, Edwin Fine wrote:

Well, I found a bizarre and (to me) totally inexplicable way to get rid
of the warning. This is from the quiz turtle_view.rb file. Adding an
explicit return value (any value; I used nil) to the draw method gets
rid of the warning. WTF???

   # Transform the turtle's track into TkcLine objects and add them to
the
   # canvas' display list. This method expects a track to be an array of
   # the form
   # track ::= [segment, segment, ...]
   # segment ::= [point, point, ...]
   # point ::= [x, y]
   # where x and y are floats.
   def draw
     @turtle.track.each do |seqment|
        if seqment.size > 1
           pts = seqment.collect { |pt| transform(pt) }
           TkcLine.new(@canvas, pts)
        end
     end
     nil # Adding this (or any return value) stops that @encoding
warning
   end

I just dunno. Weird.

Message-ID: <86b6e853aac70b4d0b579cec71b1afd1@ruby-forum.com>

Well, I found a bizarre and (to me) totally inexplicable way to get rid
of the warning. This is from the quiz turtle_view.rb file. Adding an
explicit return value (any value; I used nil) to the draw method gets
rid of the warning. WTF???

Do you use the method "draw" at the end of callback operation?
If so, the method returns the result to the Tcl/Tk interpreter.
Then, the result (a Ruby's object) is converted to a string.

Usually, an object of TkObject or its subclasses is converted to
its @path value which is a string.
And when pass the string to Tcl/Tk side, @encoding check is required.

nil or a numeric doesn't need @encoding check.

···

From: Edwin Fine <efine145-nospam01@usa.net>
Subject: Re: tk.rb warning in Turtle Graphics (#104)
Date: Sat, 2 Dec 2006 18:28:44 +0900
--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

Here's my solution. It passes all the unit tests, including the new
ones. I added a further unit test to try to force divide by zero errors
in the atan(y/x) calculation, and to see what happens if there is
"stupid" input, like two consecutive "face [100,0]" commands... hey,
stuff happens...
I must say, this turned out to be a bit more involved than I thought it
would. I kept tripping over the differences between turtle space angles
and "normal" angles.

As noted elsewhere, I also added a "nil" return to the turtle_viewer.rb
view method to eliminate most pesky Tk warnings. Thanks to Hidetoshi
NAGAI for explaining why this does what it does.

<code>
class Turtle
  include Math # turtles understand math methods
  DEG = Math::PI / 180.0

  NORTH = 0.0
  HOME = [0, 0]

  alias run instance_eval

  def initialize
    self.clear
    self.pen_up
  end

  attr_reader :track, :xy, :heading

  # Place the turtle at [x, y]. The turtle does not draw when it changes
  # position.
  def xy=(coords)
    @xy = validate_coords(coords)
  end

  # Set the turtle's heading to <degrees>. Heading is measured CLOCKWISE
from NORTH!
  def heading=(degrees)
    @heading = validate_degrees(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_up = true
  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_up = false
  end

  # Is the pen up?
  def pen_up?
    @pen_up
  end

  # Is the pen down?
  def pen_down?
    not self.pen_up?
  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 = HOME
    self.heading = NORTH
  end

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

  # Turn right through the angle <degrees>.
  def right(degrees)
    h = self.heading + validate_degrees(degrees)
    self.heading = normalize_degrees(h)
  end

  # Turn left through the angle <degrees>.
  def left(degrees)
    h = self.heading - validate_degrees(degrees)
    self.heading = normalize_degrees(h)
  end

  # Move forward by <steps> turtle steps.
  def forward(steps)
    validate_steps(steps)
    normal_radians = to_rad(flip_turtle_and_normal(@heading))
    new_pt = [@xy[0] + steps * cos(normal_radians),
              @xy[1] + steps * sin(normal_radians)]

    add_segment_to_track @xy, new_pt if self.pen_down?
    @xy = new_pt
  end

  # Move backward by <steps> turtle steps.
  def back(steps)
    validate_steps(steps)

    normal_radians = to_rad(flip_turtle_and_normal(@heading))
    new_pt = [@xy[0] - steps * cos(normal_radians),
              @xy[1] - steps * sin(normal_radians)]

    if self.pen_down?
      add_segment_to_track @xy, new_pt
    end

    @xy = new_pt
  end

  # Move to the given point.
  def go(pt)
    validate_coords(pt)
    add_segment_to_track(self.xy, pt) if self.pen_down?
    self.xy = pt
  end

  # Turn to face the given point.
  def toward(pt)
    validate_coords(pt)
    delta_x = (pt[0] - self.xy[0]).to_f
    delta_y = (pt[1] - self.xy[1]).to_f
    return if delta_x.zero? and delta_y.zero?

    # Handle special cases
    case
    when delta_x.zero? # North or South
      self.heading = delta_y < 0.0 ? 180.0 : 0.0
    when delta_y.zero? # East or West
      self.heading = delta_x < 0.0 ? 270.0 : 90.0
    else
      # Calcs are done in non-turtle space so we have to flip afterwards
      quadrant_adjustment = if delta_x < 0.0 then 180 elsif delta_y <
0.0 then 360.0 else 0.0 end
      self.heading = flip_turtle_and_normal(to_deg(atan(delta_y /
delta_x)) + quadrant_adjustment)
    end
  end

  # Return the distance between the turtle and the given point.
  def distance(pt)
    # Classic Pythagoras
    sqrt((pt[0] - @xy[0]) ** 2 + (pt[1] - @xy[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

  # Validations

  def validate_coords(coords)
    unless coords.respond_to? :[] and
           coords.respond_to? :length and
           coords.length == 2 and
           coords[0].kind_of? Numeric and
           coords[1].kind_of? Numeric
      raise(ArgumentError, "Invalid coords #{coords.inspect}, should be
[num, num]")
    end
    coords
  end

  def validate_degrees(degrees)
    raise(ArgumentError, "Degrees must be numeric") unless
degrees.kind_of? Numeric
    normalize_degrees(degrees)
  end

  def validate_steps(steps)
    raise(ArgumentError, "Steps must be numeric") unless steps.kind_of?
Numeric
  end

  # Normalizations

  # Flip between turtle space degrees and "normal" degrees (symmetrical)
  def flip_turtle_and_normal(degrees)
    (450.0 - degrees) % 360.0
  end

  # Normalize degrees to interval [0, 360)
  def normalize_degrees(degrees)
    degrees += 360.0 while degrees < 0.0
    degrees % 360.0
  end

  def add_segment_to_track(start, finish)
    @track << [ start, finish ]
  end

  def to_rad(deg)
    deg * DEG
  end

  def to_deg(rad)
    rad / DEG
  end
end
</code>

···

---------------
Here's the extra test case:

<code>
  def test_edge_cases
    east = [100, 0]
    west = [-100, 0]
    north = [0, 100]
    south = [0, -100]
    @turtle.home
    assert_equal(0, @turtle.heading.round)
    assert_nothing_raised { @turtle.face [0, 0] }
    assert_equal(0, @turtle.heading.round)
    assert_nothing_raised { @turtle.face north }
    assert_equal(0, @turtle.heading.round)
    @turtle.face east
    assert_nothing_raised { @turtle.face east }
    assert_equal(90, @turtle.heading.round)
    @turtle.face south
    assert_nothing_raised { @turtle.face south }
    assert_equal(180, @turtle.heading.round)
    @turtle.face west
    assert_nothing_raised { @turtle.face west }
    assert_equal(270, @turtle.heading.round)
  end
</code>

--
Posted via http://www.ruby-forum.com/.

Hidetoshi NAGAI wrote:

Do you use the method "draw" at the end of callback operation?
If so, the method returns the result to the Tcl/Tk interpreter.
Then, the result (a Ruby's object) is converted to a string.

Usually, an object of TkObject or its subclasses is converted to
its @path value which is a string.
And when pass the string to Tcl/Tk side, @encoding check is required.

nil or a numeric doesn't need @encoding check.

Thanks for the explanation. This was driving me nuts :slight_smile:

···

--
Posted via http://www.ruby-forum.com/\.

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

···

On 04/12/2006, at 8:28 PM, Edwin Fine wrote:

Here's my solution.

Pete Yandell wrote:

Here's my solution.

Ok, a couple of comments.

First, your home method doesn't raise the pen as it should.

You're right. That's what I get for working when too tired... :(.

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.

I did the x-y swapping in an earlier version of the program, but I feel
the flipping is more intuitive for me. It also makes it easier for me to
see how angles change between turtle space and conventional space.

Third, Ruby has an atan2 method that does most of what you do in your
toward method.

You learn something new every day!

Fourth, your normalize_degrees method is overkill. Try '-10 % 360' in
irb.

Thanks for pointing that out. Language specifics are sometimes quite
subtle. This is why it's good to post to RubyQuiz - I learn to do things
in a better way. Thanks for your feedback.

···

On 04/12/2006, at 8:28 PM, Edwin Fine wrote:

--
Posted via http://www.ruby-forum.com/\.

Pete Yandell wrote:

    # 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

What is the correct behavior if calling toward(pt) and @xy == pt. In
this case, atan2 returns 0.0 (North in turtle). This means that setting
the turtle to point to where it already is makes it always face North,
which seems wrong. I would think that this should be a no-op (heading
does not change).

irb(main):004:0> Math.atan2(0,0)
=> 0.0

Try this test case.

  def test_toward
    east = [100, 0]
    @turtle.face east
    assert_equal(90, @turtle.heading.round)
    assert_nothing_raised { @turtle.face [0, 0] }
    assert_equal(90, @turtle.heading.round)
  end

···

--
Posted via http://www.ruby-forum.com/\.

You bring up a good point here. Commanding the turtle to face the point where it's located is really an indeterminate operation. I think there are three reasonable responses to such a command:

1. Raise an error (because an indeterminate operation should be treated like 0/0).
2. Make it a no-op (as you suggest).
3. Accept the value returned by Math#atan2 (a show of faith in the C math library :).

Philosophically, I favor the first response because I think this situation would most likely arise from a programmer error. But it's not an error that's commonly made. Also, in implementations maintaining the turtle's location with floats, testing whether or not @xy is the same as the argument given to toward/face is rather expensive. So in practice, I take the lazy way out and go with the atan2 flow.

However, I would not fault an implementation that goes one of the other routes.

Regards, Morton

···

On Dec 4, 2006, at 6:21 PM, Edwin Fine wrote:

Pete Yandell wrote:

    # 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

What is the correct behavior if calling toward(pt) and @xy == pt. In
this case, atan2 returns 0.0 (North in turtle). This means that setting
the turtle to point to where it already is makes it always face North,
which seems wrong. I would think that this should be a no-op (heading
does not change).

irb(main):004:0> Math.atan2(0,0)
=> 0.0

Try this test case.

  def test_toward
    east = [100, 0]
    @turtle.face east
    assert_equal(90, @turtle.heading.round)
    assert_nothing_raised { @turtle.face [0, 0] }
    assert_equal(90, @turtle.heading.round)
  end