The three rules of Ruby Q.:
-
Please do not post any solutions or spoiler discussion for this quiz
until
48 hours have passed from the time on this message. -
Support Ruby Q. by submitting ideas as often as you can:
- Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby T. follow the discussion.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
This week’s Ruby Q. is to build a complete game with the help of your
fellow
Ruby P.mers. This is a multipart Ruby Q. 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 Q. 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.