Get Off My Lawn

Adventures of a Curmudgeon

Writing a Bit of Ruby Code Does the Body Good...

Last night I spend about 4 hours writing some Ruby code to solve a particular Coding Problem. That time includes reading the problem description, creating the design, writing specs and code, and looking up API calls. It was mad fun.The coding problem dealt with a couple of Mars rovers. The problem description was very detailed; I wish most clients had as detailed requirements.

Design Time

After reading the problem description a couple of times to get a feel for what I’m trying to solve I opened a new document in TextMate (which rocks and every Mac developer should own a copy) and started putting down my thoughts in a semi-freeform manner. I could see I would have a Rover, of course. However I also seen that the Rover would have a Motor and a Navigational System; the Navigational System would also have a GPS Unit. I then wrote down notes on movement and direction; I also defined movement in a particular direction in terms of the x,y coordinates. Coding Time!

BDD Time

I like using Rspec and wanted to use it for my coding solutions. It provides a nice narrative over my code base; what I needed since I would not be creating a ‘Main’ script. So having just wrote down some design notes I thought to myself, “What is the driving context? What’s going to manage my Rovers?” I came up with the idea of a Mission - back to my design notes.

Mission Design

What is this Mission object and what does it do? Well, it defines how many Rovers are going to be used. It also defines how large the movement area will be. It’s also responsible for issuing the commands to the Rovers and handling any errors and output. Cool - now back to coding…Coding:

I created a Mission Spec but then switched gears. I thought I should probably define my Rover first to make sure it knows how to handle all the input I’m (the Mission) is going to throw at it. Super! I create a Rover Spec. Then in an iterative fashion I create my tests and the simplest implementation that works.

Iteration 1

First my Rover has a default location and heading and knows how to report this information separately and combined. Pretty simple - all hard coded values because the spec just expects values. [sourcecode language=”ruby”] it “should be pointing North” do @rover.heading.should == ‘N’ end it “should be located on 0 0” do @rover.location.should == ‘0 0’ end it “should be positioned at 0 0 N” do @rover.position.should == ‘0 0 N’ end[/sourcecode] ##### Iteration 2

My Rover learns to spin to the left or the right. This is a little trickier since it changes the heading of my rover. I add the Navigational System because it knows about headings, or compass directions. [sourcecode language=”ruby”] it “should be pointing West” do @rover.input(‘L’) @rover.position.should == ‘0 0 W’ end it “should be pointing East” do @rover.input(‘R’) @rover.position.should == ‘0 0 E’ end it “should be pointing South, spinning Left” do @rover.input(‘L’).input(‘L’) @rover.position.should == ‘0 0 S’ end it “should be pointing South, spinning Right” do @rover.input(‘R’).input(‘R’) @rover.position.should == ‘0 0 S’ end it “should be pointing East, spinning Left” do @rover.input(‘L’).input(‘L’).input(‘L’) @rover.position.should == ‘0 0 E’ end it “should be pointing West, spinning Right” do @rover.input(‘R’).input(‘R’).input(‘R’) @rover.position.should == ‘0 0 W’ end it “should be pointing North, spinning Left then Right” do @rover.input(‘L’).input(‘R’) @rover.position.should == ‘0 0 N’ end it “should be pointing North, completing a full spin” do 4.times { @rover.input(‘L’) } @rover.position.should == ‘0 0 N’ end[/sourcecode] ##### Iteration 3

The Rover learns to move. Now I have to add the GPS Unit since it knows where the rover is located. It was at this time that I realized, for this exercise the Motor class adds no value to the solution. I leave it in anyways. I had to tweak the Navigational system so that it knew the size of the movement area. I did this because I didn’t want my Rover to travel beyond the boundaries of the movement area. [sourcecode language=”ruby”] it “should not move pass border” do @rover.position.should == ‘0 0 N’ @rover.input(‘M’) @rover.position.should == ‘0 0 N’ end it “should move one unit” do @rover.set_grid_system(1, 1) @rover.position.should == ‘0 0 N’ @rover.input(‘M’) @rover.position.should == ‘0 1 N’ @rover.input(‘M’) @rover.position.should == ‘0 1 N’ end[/sourcecode] ##### Iteration 4

I felt at this point I had enough behavior to start implementing the Mission spec. I started creating the spec and the corresponding Mission object. I quickly discovered I didn’t have a way to set the size of the movement area; Rover, Navigational System updated. I then discovered I didn’t have a way to set the initial position of a Rover; back to the Rover Spec, implemented. Lastly I wanted to process the command of movements as entered: a single line of characters. This was added without a corresponding spec test (bad Mel). [sourcecode language=”ruby”] it “should have an initial position of 3 3 E” do @rover.position.should == ‘0 0 N’ @rover.initial_position(‘3 3 E’) @rover.position.should == ‘3 3 E’ end[/sourcecode] ##### Iteration 5

Thank goodness I had tests! My movement wasn’t working as expected. My initial set of tests didn’t cover a particular case. However, when implementing the Mission Spec and not getting the expected results I was able to discover a typo if my movement logic. [sourcecode language=”ruby”] before(:each) do @mission = @mission.define_plateau(5,5) end it “should command Rover 1 from [1 2 N] to [1 3 N]” do rover_1 = @mission.rover_1 rover_1.initial_position(‘1 2 N’) rover_1.issue_command(‘LMLMLMLMM’) rover_1.position.should == ‘1 3 N’ end it “should command Rover 2 from [3 3 E] to [5 1 E]” do rover_2 = @mission.rover_2 rover_2.initial_position(‘3 3 E’) rover_2.issue_command(‘MMRMMRMRRM’) rover_2.position.should == ‘5 1 E’ end[/sourcecode]  So all in all I had a great time last night (I’m sure I’m missing some details - I’m sleepy for goodness sake). I’ll try to add bits of my code to this blog if I can figure out how to do code highlighting.