# Space Merchant (#71)

**URL:** <https://www.ruby-forum.com/t/space-merchant-71/85965>\
**Category:** Ruby\
**Created:** [March 23, 2006, 2:53pm UTC](https://www.ruby-forum.com/t/space-merchant-71/85965 "2006-03-23T14:53:08Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [March 23, 2006, 2:53pm UTC](https://www.ruby-forum.com/t/space-merchant-71/85965/1 "2006-03-23T14:53:08Z")

</div>

The primary focus of this quiz, for me, was to see how well a handful of  
developers could quickly throw something together, without much  
knowledge of  
what the other guys were doing. I think it went very well. I forgot  
several  
basic things in my specification (like how Planet and Station needed a  
name()  
accessor), but convention and common sense seemed to get us through with  
little  
trouble. Too cool.

Obviously, I can’t show all the code that was written this week.  
Instead, I  
will try to hit on some highlights.

```
Development in Isolation

```

Since we were each just building a part of the whole, one of the big  
questions  
became, how do I test my part? I built the Station, which doesn’t  
really  
require much from the other pieces. Sector and Galaxy help you move  
from  
Station to Station, but that turns out to be trivial to bypass. I  
really just  
needed to pretend I was docking at Station after Station. To do that, I  
added  
the following code to the end of station.rb:

```
if __FILE__ == $PROGRAM_NAME
  player = {:credits => 1000}

  loop do
    if player[:location].nil?
      player[:location] = SpaceMerchant::Station.new(nil, "Test")
    end

    player[:location].handle_event(player)
  end
end

```

The idea here is that when you lift off from a Station, you will go back  
into  
the Sector. So if we pass Station some Sector substitute that is easy  
to watch  
for, nil for example, we can just replace that object with a newly  
constructed  
Station whenever we see it. This simulates flying from Sector to  
Sector,  
docking at Stations.

Some pieces depended on the others more heavily though, requiring more  
complete  
solutions for testing. Ross B. built Galaxy, which requires at  
least a  
minimal representations of the other celestial objects. Ross solved  
this by  
mocking the other objects with the needed functionality:

```
  if $0 == __FILE__
    # The comparable stuff is needed only by the tests,
    # not the Galaxy impl itself.
    class Named #:nodoc: all
      def initialize(sector, name); @name = name.to_s; end
      def name; @name; end
      alias :to_s :name
      def inspect; "#{self.class.name}:#{@name}"; end
      def ==(o); name == o.name; end
      def <=>(o); name <=> o.to_s; end
    end

    class Sector < Named #:nodoc: all
      def initialize(name, location = nil)
        super(nil, name)
        @location, @planets, @stations, @links = location, [], [], []
      end
      attr_accessor :location, :planets, :stations, :links
      def add_planet(planet); @planets << planet; end
      def add_station(station); @stations << station; end
      def link(o); @links << o; end
      def ==(o)
        begin
          name == o.name &&
            planets == o.planets &&
            stations == o.stations &&
            links.length == o.links.length
        rescue NoMethodError
          false
        end
      end
    end

    class Planet < Named #:nodoc: all
    end

    class Station < Named #:nodoc: all
    end

    # ...

```

Ross started with the minimal Named functionality that all objects  
share. I  
would have provided this in the quiz, if I was as smart as Ross. From  
there,  
Ross just adds in the functionality Galaxy requires. Note how unused  
details  
(like the sector parameter to new()) are just casually ignored. The  
goal is to  
build only what is needed to test the Galaxy implementation.

```
The Singleton Shortcut

```

I know how we all love a good method\_missing() trick, so here’s my  
favorite for  
this week, again from Ross:

```
  class Galaxy
    include Singleton

    # ...

    class << self
      # tired of writing 'Galaxy.instance' in tests...
      def method_missing(sym, *args, &blk) #:nodoc:
        instance.send(sym, *args, &blk)
      end
    end

    # ...

```

Obviously, there are other solutions to the problem the comment  
describes, but  
this particular trick made for a nice interface, I thought. Observe:

```
Galaxy.instance.find_planets { |planet| ... }
# ... becomes...
Galaxy.find_planets { |planet| ... }

```

That might come in handy with other uses of Singleton, I think.

```
The Big Event

```

Another detail of this quiz the solvers had to work with was how do  
handle  
events. Here’s a handle\_event() method for Sector, by Timothy B.:

```
    # ...

    def handle_event ( player )
      player[:visited_sectors] ||= []
      player[:visited_sectors] << self \
        unless player[:visited_sectors].find { |sector| sector == self 

```

}  
print\_menu  
choice = gets.chomp  
case choice  
when /d/i: choose\_station  
when /l/i: choose\_planet  
when /p/i: plot\_course  
when /q/i: throw(:quit)  
when /\d+/: warp player, choice  
else invalid\_choice  
end  
end

```
    # ...

```

Aside from the elegant menu dispatch at the end of the method, the main  
point of  
interest is the first line. We all had to add our individual elements  
to the  
Player object as needed, which required a little defensive programming.  
When  
the Player first arrives in a Sector, there is no :visited\_sectors key  
(the game  
script doesn’t create one). This is probably a sign that I should have  
provided  
an initialization hook in the quiz, but optional assignments like the  
above  
still might have been needed for things not known in advance. Luckily  
the ||=  
operator is just perfect for this kind of work.

I won’t show all the all of the event methods used above, but here is  
one of  
them:

```
	  # ...

	  def choose_station
	    player = Player.instance
	    puts "There are no stations to dock with!" if @stations.empty?
	    if @stations.size == 1
	      dock @stations[0], player
	    else
	      @stations.each_with_index do |station, index|
	        puts "(#{index + 1}) #{station.name}"
	      end
	      puts "Enter the number of the station to dock with: "

	      station_index = gets.chomp.to_i - 1
	      if @stations[station_index]
	        dock @stations[station_index], player
	      else
	        puts "Invalid station."
	      end
	    end
	  end

	  # ...

```

I really liked how this method would just intelligently make the choice,  
if  
there was only one, or prompt the user when a decision needed to be  
made. This  
made for a better playing experience for sure.

```
Manufacturing Fun and Destruction

```

The final aspect of this quiz was, of course, innovation. I left the  
specification very open in the hopes that someone would grab the ball  
and run…

```
  class UsableItem
    attr_reader :rarity, :name, :description

    def initialize (name, description = "", rarity = 0.7, &block)
      @effect = block if block_given?
      @name = name
      @description = description
      @rarity = rarity
    end

    def use (player)
      if @effect
        @effect.call player
      else
        puts "#{name} has no effect."
      end
    end

    def to_s
      name
    end
  end

```

Obviously, that is just a name, description, and rarity attached to a  
block  
(from Timothy B.'s planet.rb), but just look at this example of the  
earth-shattering fun to be had with an object like this:

```
  # ...

  omega = SpaceMerchant::UsableItem.new( "Omega",
                                         "Don't push that button. 

```

Please.",  
0.9 ) do |player|  
planet = player[:location]  
player[:location] = planet.sector  
puts  
puts “You hear a terrible rumbling as the Vogon constructor fleet”  
puts “descends upon #{planet.name}. You scramble to your”  
puts “ship and launch just in time to avoid becoming space dust.”  
puts  
player[:location].planets.slice!(player[:location].planets.index(planet))  
end

```
  # ...

```

I love it.

A big thank you to all who played with my pet project. Hopefully you  
didn’t  
blow up your planet doing so.

Tomorrow we will continue our focus on essential Ruby programming skills  
with  
Breaking and Entering 101…

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [March 23, 2006, 4:15pm UTC](https://www.ruby-forum.com/t/space-merchant-71/85965/2 "2006-03-23T16:15:01Z")

</div>

On Thu, 2006-03-23 at 22:53 +0900, Ruby Q. wrote:

> The primary focus of this quiz, for me, was to see how well a handful of  
> developers could quickly throw something together, without much knowledge of  
> what the other guys were doing. I think it went very well.

Definitely agree 🙂 Thanks for another fun and (for me, at least)  
educational quiz, and for the nice write-up. This one fair took me back  
to my days as a sysop (though I don’t actually recall running TradeWars,  
only the enormous phone bills that came of all that echomail :)).
