The three rules of Ruby Quiz:

1.  Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2.  Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3.  Enjoy!

Suggestion:  A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

This week's Ruby Quiz is to build a complete game with the help of your fellow
Ruby Programmers.  This is a multipart Ruby Quiz all in one week.  Each person
should select one task they wish to solve and leave the other tasks for other
solutions.

	The Game

When I was young, I loved to play BBS door games.  A very popular game I was
hooked on was called TradeWars 2002.  While the BBS is pretty much a thing of
the past, that game has been cloned in some form or another for just about every
platform that ever existed.

The premise of the game is simple:  you play a space trader flying around the
galaxy buying and selling goods.  The main goal is to find places to buy goods
at a low price and then unload those goods at another location with a higher
price, turning a profit.  Most versions of the game allow you to use that money
to buy bigger and better space ships, which in turn allows you to carry more
goods and make bigger profits.  Many versions of the game don't have a clear
ending, though I have seen a version that eventually allowed you to buy a moon
to retire on.

	Common Elements

The thread tying all the pieces of the game together is the Player object.  It
is the centralized storage for game state information, like how much money the
player currently has.

The other item we need is a trivial event loop.  The options a player has
available at any given time are a function of where they are.  For example,
while flying through space the player probably has commands to choose a
destination, but when docked with a space station the commands will likely
center around buying and selling goods.

The following code provides both elements:

	#!/usr/local/bin/ruby -w
	
	# space_merchant.rb
	
	require "singleton"
	
	module SpaceMerchant
	  VERSION = "1.0"
	
	  # We will use a basic Hash, but feel free to add methods to it.
	  class Player
	    instance_methods.each { |meth| undef_method(meth) unless meth =~ /^__/ }
	    
	    include Singleton
	    def initialize
	      @game_data = Hash.new
	    end
	    
	    def method_missing( meth, *args, &block )
	      @game_data.send(meth, *args, &block)
	    end
	  end
	end
	
	if __FILE__ == $0
	  require "galaxy"   # Task 1
	  require "sector"   # Task 2
	  require "station"  # Task 3
	  require "planet"   # Task 4
	  
	  # collect beginning player information
	  player = SpaceMerchant::Player.instance
	  
	  puts
	  puts "Welcome to Space Merchant #{SpaceMerchant::VERSION}, " + 
	       "the Ruby Quiz game!"
	  puts
	  
	  print "What would you like to be called, pilot?  "
	  loop do
	    name = gets.chomp
	    
	    if name =~ /\S/
	      player[:name] = name
	      
	      puts "#{player[:name]} it is."
	      puts
	      
	      puts "May you find fame and fortune here in the Ruby Galaxy..."
	      puts
	      
	      break
	    else
	      print "Please enter a name:  "
	    end
	  end
	  
	  player[:credits]  = 1000
	  # we initialize player[:location], it should be changed to move the player
	  player[:location] = SpaceMerchant::Galaxy.instance.starting_location
	  
	  catch(:quit) do  # use throw(:quit) to exit the game
	    # primary event loop
	    loop { player[:location].handle_event(player) }
	  end
	end

As you can see in the event loop, all Sectors, Stations, and Planets need to
support a handle_event() method that gives the player some choices, fetches an
action from the keyboard, and responds appropriately.

The other four pieces I leave to you...

	SpaceMerchant::Galaxy (Task 1)

We need an object representing the play-space as a whole.  The Galaxy is
responsible for creating all of the game locations and allowing event code to
access these locations.

There should only be one Galaxy, so please make it a Singleton (support an
instance() class method to retrieve it).

The first concern of the Galaxy is to construct Sector, Planet and Station
objects.  Sectors are pieces of space the players can move between.  Think of
them as squares on the game board.  These areas must be connected, so the player
can move from location to location.  Sectors will support these creation
methods, to aid this process:

	Sector::new( name, location = nil )
	Sector#link( other_sector )
	Sector#add_planet( planet )
	Sector#add_station( station )

Don't sweat too much over the names for the Sectors, TradeWars simply numbered
them:  Sector 1, Sector 2, ..., Sector 1051, ...  This works out surprisingly
well because players can enter letter commands for game actions (say l for land)
and numbers to move to a new Sector.

The location parameter is an optional way to divide Sectors into groups.  For
example, Sectors 1 through 10 were always "Fed Space" in TradeWars.

The add_*() methods are for placing Planet and Station objects in the Sector. 
Both are trivial to construct (again be creative with the names):

	Planet.new( sector, name )
	Station.new( sector, name )

Let's keep the number objects fairly small, just so we don't overwhelm the
player.  Something like up to five Planets, possibly one Station, and a maximum
of six links to nearby Sectors sounds sane to me (but use your best judgement). 
Of course, a Sector can be empty.

Please provide these iterators to help the other classes locate objects as well:

	Galaxy::find_sectors { |sector| ... }
	Galaxy::find_planets { |planet| ... }
	Galaxy::find_stations { |station| ... }

Finally, the Galaxy should provide a pathfinding algorithm, for use in
navigation:

	Galaxy::find_path( start_sector, finish_sector, *avoid_sectors )

This method should return an Array of Sectors beginning with the start_sector
and ending with the finish_sector.  If the optional avoid_sectors are provided,
they should not be used.  It's okay to return nil, if a path could not be found,
but this should only be possible with avoid_sectors.

Random idea:  some games have wormholes, one way links between Sectors.  This
can make for very interesting layouts.  Just be careful if you add wormholes to
make sure all Sectors can still be reached from all other Sectors.

	SpaceMerchant::Sector (Task 2)

(See Galaxy for the creation methods Sector needs to support.)

The Sector is the main interface for moving around the Galaxy.  The Sector
should show the player their location, and request a command:

	Sector 302
	The Reaches
	
	Station:  ABC Station
	Planets:  Myr III, Myr IV, Myr VII
	
	(D)ock with station
	(L)and on planet
	(P)lot a course
	
	(Q)uit game
	
	Or warp to nearby sector:  [4], [25], 1021, [9919]

Feel free to deviate from that in anyway you choose.

With the Sector you are in charge of how the player can move about.  Should they
only be allowed to move to nearby Sectors or can they plot long range courses? 
Perhaps only to Sectors they have visited before?  You decide.

Any game level functions also belong here.  Quit is obvious, but can you think
of an easy way to support save?

Random ideas:  consider supporting random encounters with computer controlled
ships to make the galaxy feel more alive.  "Unidentified vessel, this is the
Galactic Patrol and we suspect you are transporting illegal cargo...  Prepare to
be boarded!"

Also consider supporting other space features like nebulas, black holes,
asteroid belts, etc.  How should these affect the player?

	SpaceMerchant::Station (Task 3)

(See Galaxy for the constructor Station needs to support.)

Stations are where the player can buy and sell goods.  There may be some set
categories of goods available in the Galaxy, but an individual Station should
probably just offer to trade in a subset of those.

Some games have Stations set prices based on where they are in the Galaxy while
other games have the prices fluctuate everywhere over time.  You can even have
Stations set their prices based on demand, which could rise and fall as the
player trades there.

The player's ship should probably have a limited capacity of goods it can carry
at a time.  I purposefully haven't set this, so that Station implementations
could flesh out the player's ship as they saw fit.  Perhaps some Stations could
even offer ship upgrades.

Random idea:  The original TradeWars game even allowed players to choose a
criminal path.  They could try stealing goods from a Station, to trade
elsewhere.  There were penalties for getting caught though and eventually word
of your crimes got around and you weren't safe from the other denizens of space.

	SpaceMerchant::Planet (Task 4)

(See Galaxy for the constructor Planet needs to support.)

Planets have had numerous functions in the various incarnations of space trading
games.  In the spirit of that, I'm leaving this section as a sort of
free-for-all.

My suggestion is to use the planets as a kind of a quest engine.  Allow the
player to pick-up unique cargos or people and transport them to other planets in
the Galaxy.  See if you can get these quests to build on each other and form a
basic plot for the game.

Random idea:  Some games have had items for sale which allowed the creation and
destruction of planets.  You might even be able to tie that into the missions
somehow.

	Sharing Your API

When you have finished a task, you are invited to send in a message telling
others what you built, even if the spoiler period has not yet passed.  You may
include documentation for some methods your object provides, or detail some
variables you set on the Player.  This may help others to choose a different
task and take advantage of features you provided.