Commit b2b7c87

mo k <mo@mokhan.ca>
2012-11-05 04:35:46
refactor how edge of the map calculation occurs and fix bug when moving forward beyond the map when facing north or east.
1 parent 47932cb
lib/east.rb
@@ -8,9 +8,8 @@ class East
   def rotate(degrees)
     degrees > 0 ? turn_right : turn_left
   end
-  def forward(location)
-    location[:x] = location[:x] + 1
-    location
+  def forward(location, plateau)
+    plateau.increment(:x, location)
   end
   def represents?(direction)
     :east == direction
lib/location.rb
@@ -1,15 +1,23 @@
 class Location
   attr_reader :location, :heading
+
   def initialize(x, y, heading)
     @location = {:x => x, :y => y}
     @heading = heading
   end
+
+  def forward(plateau)
+    @heading.forward(self, plateau)
+  end
+
   def rotate(degrees)
     @heading = @heading.rotate(degrees)
   end
+
   def is_facing(direction)
     @heading.represents? direction
   end
+
   def to_s
     "#{@location[:x]} #{@location[:y]} #{@heading}"
   end
lib/north.rb
@@ -8,9 +8,8 @@ class North
   def rotate(degrees)
     degrees > 0 ? turn_right : turn_left
   end
-  def forward(location)
-    location[:y] = location[:y].to_i + 1
-    location
+  def forward(location, plateau)
+    plateau.increment(:y, location)
   end
   def represents?(direction)
     :north == direction
lib/plateau.rb
@@ -10,25 +10,35 @@ class Plateau
     @directions = {:N => North.new, :E => East.new, :W => West.new, :S => South.new}
   end
 
-  def move_forward( heading, location)
-    new_location = heading.forward(location.clone)
-
-    adjust(location, new_location, :x)
-    adjust(location, new_location, :y)
+  def move_forward(location)
+    location.forward(self)
   end
 
   def deploy_rover_to(heading, x, y)
-    Rover.new(@directions[heading.to_sym],  x,  y, self)
+    Rover.new(direction_for(heading),  x,  y, self)
+  end
+
+  def increment(axis, location)
+    next_location = location.location[axis] + 1
+    if(next_location > @map[axis])
+      location.location[axis] = 0
+    else
+      location.location[axis] = next_location
+    end
   end
 
-  private 
-  def adjust(location, new_location, axis)
-    if(new_location[axis] > @map[axis])
-      location[axis] = new_location[axis] - @map[axis]
-    elsif (new_location[axis] < 0)
-      location[axis] = @map[axis]
+  def decrement(axis, location)
+    next_location = location.location[axis] - 1
+    if (next_location < 0)
+      location.location[axis] = @map[axis]
     else
-      location[axis] = new_location[axis]
+      location.location[axis] = next_location
     end
   end
+
+  private
+
+  def direction_for(heading)
+    @directions[heading.upcase.to_sym]
+  end
 end
lib/rover.rb
@@ -10,7 +10,7 @@ class Rover
   end
 
   def drive
-    @plateau.move_forward(@location.heading, @location.location)
+    @plateau.move_forward(@location)
   end
 
   def is_facing(direction)
lib/south.rb
@@ -8,9 +8,8 @@ class South
   def rotate(degrees)
     degrees > 0 ? turn_right : turn_left
   end
-  def forward(location)
-    location[:y] = location[:y] - 1
-    location
+  def forward(location, plateau)
+    plateau.decrement(:y, location)
   end
   def represents?(direction)
     :south == direction
lib/west.rb
@@ -8,9 +8,8 @@ class West
   def rotate(degrees)
     degrees > 0 ? turn_right : turn_left
   end
-  def forward(location)
-    location[:x] = location[:x] - 1
-    location
+  def forward(location, plateau)
+    plateau.decrement(:x, location)
   end
   def represents?(direction)
     :west == direction
spec/features/navigation.feature
@@ -4,13 +4,29 @@ Feature: Navigation
   I want to find out the final coordinates.
 
   Scenario Outline: Navigate a rover
-    Given the plateau is <start_x> by <start_y>
+    Given the plateau is <x> by <y>
     And the starting position is '<starting_position>'
     And I move "<instructions>"
     When I tell the rover to travel
     Then the rovers final position should be '<result>' on the screen.
 
     Examples:
-      | start_x | start_y | starting_position | instructions  | result |
-      | 5       | 5       | 1 2 N             | LMLMLMLMM     | 1 3 N  |
-      | 5       | 5       | 3 3 E             | MMRMMRMRRM    | 5 1 E  |
+      | x | y | starting_position | instructions  | result |
+      | 5 | 5 | 1 2 N             | LMLMLMLMM     | 1 3 N  |
+      | 5 | 5 | 3 3 E             | MMRMMRMRRM    | 5 1 E  |
+      | 1 | 1 | 0 0 N             | M    | 0 1 N  |
+      | 1 | 1 | 0 0 E             | M    | 1 0 E  |
+      | 1 | 1 | 0 0 W             | M    | 1 0 W  |
+      | 1 | 1 | 0 0 S             | M    | 0 1 S  |
+      | 1 | 1 | 1 0 N             | M    | 1 1 N  |
+      | 1 | 1 | 1 0 E             | M    | 0 0 E  |
+      | 1 | 1 | 1 0 W             | M    | 0 0 W  |
+      | 1 | 1 | 1 0 S             | M    | 1 1 S  |
+      | 1 | 1 | 1 1 N             | M    | 1 0 N  |
+      | 1 | 1 | 1 1 E             | M    | 0 1 E  |
+      | 1 | 1 | 1 1 W             | M    | 0 1 W  |
+      | 1 | 1 | 1 1 S             | M    | 1 0 S  |
+      | 1 | 1 | 0 1 N             | M    | 0 0 N  |
+      | 1 | 1 | 0 1 E             | M    | 1 1 E  |
+      | 1 | 1 | 0 1 W             | M    | 1 1 W  |
+      | 1 | 1 | 0 1 S             | M    | 0 0 S  |
spec/unit/east_spec.rb
@@ -4,12 +4,13 @@ describe East do
   let(:sut){ East.new }
 
   context "when moving forward" do
+    let(:plateau) { Plateau.new(5, 5) }
     it "should move to the next position" do
-      @location[:x].should == 1
+      @location.location[:x].should == 1
     end
     before do
-      @location = {:x => 0, :y => 0}
-      sut.forward(@location)
+      @location = Location.new(0, 0, sut)
+      sut.forward(@location, plateau)
     end
   end
   context "when turning right" do
spec/unit/north_spec.rb
@@ -3,12 +3,13 @@ require "spec_helper"
 describe North do
   let(:sut) { North.new }
   context "when moving forward" do
+    let(:plateau) { Plateau.new(5, 5) }
     it "should move to the next position" do
-      @location[:y].should == 4
+      @location.location[:y].should == 4
     end
     before do
-      @location = {:x => 0, :y => 3}
-      sut.forward(@location)
+      @location = Location.new(0, 3, sut)
+      sut.forward(@location, plateau)
     end
   end
   context "when turning right" do
spec/unit/plateau_spec.rb
@@ -6,67 +6,56 @@ describe Plateau do
   context "when moving forward" do
     context "when the next position is to far east" do
       it "should move to the other side of the board" do
-        @location[:x].should == 1
-        @location[:y].should == 0
+        @location.location[:x].should == 0
+        @location.location[:y].should == 0
       end
       before do
-        @heading = fake
-        @location = {:x => 3, :y => 0}
-        @heading.stub(:forward).with(@location).and_return({:x => 4, :y => 0})
-
-        sut.move_forward(@heading, @location)
+        @location = Location.new(3, 0, East.new)
+        sut.move_forward(@location)
       end
     end
 
     context "when the next position is to far west" do
       it "should move to the other side of the board" do
-        @location[:x].should == 3
-        @location[:y].should == 0
+        @location.location[:x].should == 3
+        @location.location[:y].should == 0
       end
       before do
-        @heading = fake
-        @location = {:x => 0, :y => 0}
-        @heading.stub(:forward).with(@location).and_return({:x => -1, :y => 0})
-        sut.move_forward(@heading, @location)
+        @location = Location.new(0, 0, West.new)
+        sut.move_forward(@location)
       end
     end
 
     context "when the next position is to far north" do
       it "should move to the other side of the board" do
-        @location[:x].should == 0
-        @location[:y].should == 1
+        @location.location[:x].should == 0
+        @location.location[:y].should == 0
       end
       before do
-        @heading = fake
-        @location = {:x => 0, :y => 3}
-        @heading.stub(:forward).with(@location).and_return({:x => 0, :y => 4})
-        sut.move_forward(@heading, @location)
+        @location = Location.new(0, 3, North.new)
+        sut.move_forward(@location)
       end
     end
 
     context "when the next position is to far south" do
       it "should move to the other side of the board" do
-        @location[:x].should == 0
-        @location[:y].should == 3
+        @location.location[:x].should == 0
+        @location.location[:y].should == 3
       end
       before do
-        @heading = fake
-        @location = {:x => 0, :y => 0}
-        @heading.stub(:forward).with(@location).and_return({:x => 0, :y => -1})
-        sut.move_forward(@heading, @location)
+        @location = Location.new( 0,  0, South.new)
+        sut.move_forward(@location)
       end
     end
 
     context "when the next position is just fine" do
       it "should move position forward" do
-        @location[:x].should == 1
-        @location[:y].should == 1
+        @location.location[:x].should == 1
+        @location.location[:y].should == 1
       end
       before do
-        @heading = fake
-        @location = {:x => 0, :y => 0}
-        @heading.stub(:forward).with(@location).and_return({:x => 1, :y => 1})
-        sut.move_forward(@heading, @location)
+        @location = Location.new( 1,  0, North.new)
+        sut.move_forward(@location)
       end
     end
   end
spec/unit/south_spec.rb
@@ -3,12 +3,13 @@ require 'spec_helper'
 describe South do
   let(:sut) { South.new }
   context "when driving forward" do
+    let(:plateau) { Plateau.new(5, 5) }
     it "should move forward" do
-      @location[:y].should == 0
+      @location.location[:y].should == 0
     end
     before do
-      @location = {:x => 0, :y => 1}
-      sut.forward(@location)
+      @location = Location.new(0, 1, sut)
+      sut.forward(@location, plateau)
     end
   end
   context "when turning left" do
spec/unit/west_spec.rb
@@ -4,12 +4,13 @@ describe West do
   let(:sut){ West.new }
 
   context "when moving forward" do
+    let(:plateau) { Plateau.new(5, 5) }
     it "should move to the next position" do
-      @location[:x].should == 0
+      @location.location[:x].should == 0
     end
     before do
-      @location = {:x => 1, :y => 0}
-      sut.forward(@location)
+      @location = Location.new(1, 0, sut)
+      sut.forward(@location, plateau)
     end
   end
   context "when turning right" do