Forum: Ruby Space Merchant (#71)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-17 14:41
(Received via mailing list)
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.
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2006-03-17 19:35
(Received via mailing list)
I've attached a library, GalaxyLoader, that parses a DSL for "world
builders". There's a brief example of the DSL following, and an
example world attached. The library also adds a class method
Galaxy.load to your Galaxy class. With this library, loading a galaxy
is as simple as:

  require 'galaxy_loader'
  galaxy = Galaxy.load('sample.glx')
  # do stuff with the galaxy...

The DSL is fairly simple. A galaxy can have many sectors, possibly
within regions:

  galaxy {
    region("Federation Space") {
      sector(1) { ... }
      sector(2) { ... }
    }

    region("Klingon Space") {
      sector(3) { ... }
      sector(4) { ... }
    }

    sector(5) { ... }
    sector(6) { ... }
  }

A sector can have planets, stations and neighbors:

  galaxy {
    sector(1) {
      planet "Earth"
      station "Alpha"
      neighbors 2, 3
    }
    sector(2) {
      station "Beta"
      neighbors 1, 3
    }

    sector(3) {
      neighbors 1, 2
    }
  }

And that's all there is to it! I tried to make the GalaxyLoader class
as consistent as possible, so it should be fairly easy to add further
attributes to planets and stations by following the pattern of the
existing methods.

Enjoy!

Jacob Fugal
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-18 00:40
(Received via mailing list)
On Sat, 2006-03-18 at 03:33 +0900, Jacob Fugal wrote:
> I've attached a library, GalaxyLoader, that parses a DSL for "world
> builders".

Pretty cool, beats my current random generation strategy :)

I think I have a basic Galaxy implementation right now, with most of the
requirements in there. I've made rdoc off of it and posted it at [1].
I'll probably make a few (additive) changes between now and Sunday
though.

I've left the tests in the rdoc to give some samples. It also seems to
work fine with the GalaxyLoader.

[1] http://roscopeco.co.uk/code/ruby-quiz-entries/71/rdoc/
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-20 00:25
(Received via mailing list)
I'm working on a Sector implementation at the moment.  I'll post the
RDocs for it sometime tonight, but for now it's designed to work with
Ross's Galaxy, and also assumes that Planets and Stations implement
name => String.    It also adds @visited_sectors to Player, so that
long range courses can only be plotted to (and through) previously
visited sectors.  If I have time, I may add support for one of the
other ideas (nebulas, random encounters, black holes, etc.).

Is anyone working on the Planet or Station classes?

Tim
2fa5dcd1ca7a14b99a5ed1cc26787c63?d=identicon&s=25 Jay Anderson (horndude77)
on 2006-03-20 03:43
(Received via mailing list)
Ok here is my quick SpaceMerchant::Sector class. I wasn't able to
participate in last weeks quiz (and I suggested it!) so I want to be
sure and do something in this week. I need to go through and clean this
up now, but I wanted to get this out there quick because I haven't seen
any parts besided the Galaxy put up. At the very end is the code I've
been using to tests with. Ok thanks!

-----Jay Anderson

module SpaceMerchant
    class Sector
        attr_reader :name

        def initialize(name, region)
            @name, @region = name, region
            @planets = []
            @stations = []
            @links = []
        end

        def link(other_sector)
            @links << other_sector
        end

        def add_planet(planet)
            @planets << planet
        end

        def add_station(station)
            @stations << station
        end

        def handle_event(player)
            menu = :main_menu
            while menu != :done
                menu = send menu, player
            end
        end

        def main_menu player
            puts "Sector #{name}"
            puts "#{@region.name}"
            puts
            if(@stations.length > 0) then
                puts "Station#{'s' if @stations.length>0}:
"+@stations.map{|s|s.name}.join(', ')
            end
            if(@planets.length > 0) then
                puts "Planet#{'s' if @planets.length>0}:
"+@planets.map{|p|p.name}.join(', ')
            end

            puts
            puts '(D)ock with station' if @stations.length > 0
            puts '(L)and on planet' if @stations.length > 0
            puts
            if(@links.length > 0) then
                puts 'Warp to nearby sector:
'+@links.map{|l|'['+l.name+']'}.join(', ')
            end
            puts
            puts '(Q)uit game'
            print '?'

            response = gets.chomp
            case response
            when /^d/i
                return :dock
            when /^l/i
                return :land
            when /^q/i
                puts "Are you sure you want to quit? (y/n)"
                y_or_n = ''
                while (y_or_n = gets) !~ /^(y|n)/i
                    puts "Are you sure you want to quit? (y/n)"
                end
                if y_or_n =~ /^y/i then puts 'goodbye!'; exit 0; end
                return :main_menu
            else
                new_loc = @links.select{|l| l.name == response}
                if new_loc.length > 0 then
                    puts "Travelling..."
                    player[:location] = new_loc.first
                    return :done
                else
                    puts " *** INVALID CHOICE ***"
                    return :main_menu
                end
            end
        end

        def dock player
            if @stations.length < 1 then
                puts "There are no stations to dock with"
                return :main_menu
            end
            puts "Choose station to dock with:"
            puts
            @stations.each_with_index do |s,index|
                puts "  #{index}: #{s.name}"
            end
            puts
            puts "(M)ain menu"
            print "?"

            response = gets.chomp
            choice = response.to_i
            if response =~ /^m/i then
                return :main_menu
            elsif choice.to_s != response || choice >= @stations.length
then
                puts " *** INVALID CHOICE ***"
                return :dock
            else
                puts "Docking..."
                player[:location] = @stations[choice]
                return :done
            end
        end

        def land player
            if @planets.length < 1 then
                puts "There are no planets to land on"
                return :main_menu
            end
            puts "Choose planet to land on:"
            puts
            @planets.each_with_index do |p,index|
                puts "  #{index}: #{p.name}"
            end
            puts
            puts "(M)ain menu"
            print "?"

            response = gets.chomp
            choice = response.to_i
            if response =~ /^m/i then
                return :main_menu
            elsif choice.to_s != response || choice >= @planets.length
then
                puts " *** INVALID CHOICE ***"
                return :dock
            else
                puts "Landing..."
                player[:location] = @planets[choice]
                return :done
            end
        end
    end
end


if __FILE__ == $0
    Named = Struct.new(:name)
    region = Named.new('The Region')
    s = SpaceMerchant::Sector.new('Test Sector', region)
    5.times do |i|
        s.add_planet(Named.new("planet #{i}"))
        s.add_station(Named.new("station #{i}"))
        s.link(SpaceMerchant::Sector.new("#{i}", region))
    end

    player = {}
    s.handle_event(player)
    p player
end
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-20 04:14
(Received via mailing list)
Here's the Sector implementation I was working on.  It can use a
bunch of cleaning up (choose_station and choose_planet need to be
merged together at least in part - much duplicate code there), but as
this is the second posted Sector implementation, and no one has
mentioned working on a Planet or Station implementation, I'm going to
go ahead and send in a Planet class tomorrow instead of cleaning this
up.

module SpaceMerchant
   class Sector
     attr_reader :links, :planets, :stations, :location

     def initialize ( name, location = nil )
       @name = name
       @location = location
       @links = []
       @planets = []
       @stations = []
     end

     def name
       @name.to_s
     end

     def to_s
       name
     end

     def add_planet ( planet )
       @planets << planet
     end

     def add_station ( station )
       @stations << station
     end

     def link ( to_sector )
       @links << to_sector
     end

     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

     def == ( other )
       if other.class == Sector
         self.name == other.name
       elsif other.class == String
         self.name == other
       else
         false
       end
     end

     private

     def warp ( player, sector_name )
       sector = Galaxy.instance.sectors.find { |sector| sector_name
== sector.name }
       if sector && @links.find { |sec| sector_name == sec }
         player[:location] = sector
         puts "Warping to Sector #{sector_name}..."
       elsif sector.nil?
         puts "Sector #{sector_name} does not exist."
       else
         puts "Sector #{sector_name} cannot be reached from here."
       end
       puts
     end

     def print_menu
       puts "Sector #{@name}"
       puts @location if @location
       puts

       puts "Station" + (@stations.size == 1 ? '' : 's') +
         ": " + @stations.map{|stat| stat.name}.join(', ') unless
@stations.empty?

       puts "Planet" + (@planets.size == 1 ? '' : 's') +
         ": " + @planets.map{|plan| plan.name}.join(', ')} unless
@planets.empty?
       puts "Nothing here!" if @stations.empty? && @planets.empty?
       puts

       puts "(D)ock with station" unless @stations.empty?
       puts "(L)and on planet" unless @planets.empty?
       puts "(P)lot a course"
       puts

       puts "(Q)uit game"
       puts

       puts "Or warp to nearby sector: #{@links.join(', ')}"
       puts
     end

     def invalid_choice
       puts "Please enter a valid choice."
     end

     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

     def choose_planet
       player = Player.instance
       puts "There are no planets to land on!" if @planets.empty?
       if @planets.size == 1
         land @planets[0], player
       else
         @planets.each_with_index do |planet, index|
           puts "(#{index + 1}) #{planet}"
         end
         puts "Enter the number of the planet to land on: "

         planet_index = gets.chomp.to_i - 1
         if @planets[planet_index]
           dock @planets[planet_index], player
         else
           puts "Invalid planet."
         end
       end
     end

     def land (planet, player)
       puts "Landing on #{planet.name}..."
       player[:location] = planet
     end

     def dock (station, player)
       puts "Docking at #{station.name}..."
       player[:location] = station
     end

     def plot_course
       player = Player.instance
       galaxy = Galaxy.instance
       unknown_sectors = galaxy.sectors - player[:visited_sectors]
       reachable_sectors = galaxy.find_reachable(self, unknown_sectors)
       reachable_sectors.each do |sector|
         puts "#{sector}" + sector.location ? "(#
{sector.location})" : ''
       end

       puts
       puts "Enter the sector name to which you wish to travel: "
       sector_name = gets.chomp
       destination = galaxy.sectors.find { |sector| sector ==
sector.name }
       path = galaxy.find_path( self, destination, unknown_sectors)
       puts
       unless @path.nil?
         puts "Your course:"
         path.each do |sector|
           puts "#{sector}" + sector.location ? "(#
{sector.location})" : ''
         end

         puts "Confirm course (y/n)?"
         confirm = gets.chomp =~ /y/i

         if confirm
           me[:location] = destination
           puts "Traveling to Sector #{destination}..."
         end
       else
         puts "That sector can not be reached."
       end
     end
   end
end
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-20 04:23
(Received via mailing list)
Correction to this solution:  in plot_course, @path.nil? should be
path.nil?

I should note here that plot_course is almost completely untested,
since it depends heavily on Galaxy, which I haven't completely
stubbed out.

Tim
2fa5dcd1ca7a14b99a5ed1cc26787c63?d=identicon&s=25 Jay Anderson (horndude77)
on 2006-03-20 05:29
(Received via mailing list)
Ok, here's my somewhat cleaned up version. I added a rudamentary
ability to save the game, but it would require changing the main code
to actually be able to load it.

module SpaceMerchant
    class Sector
        attr_reader :name, :region, :planets, :stations, :links

        def initialize(name, region)
            @name, @region = name, region
            @planets = []
            @stations = []
            @links = []
        end

        def link(other_sector)
            @links << other_sector
        end

        def add_planet(planet)
            @planets << planet
        end

        def add_station(station)
            @stations << station
        end

        def handle_event(player)
            @player = player
            @menu = :main_menu
            while @menu != :done
                puts '-'*60
                send @menu
            end
        end

        def main_menu
            puts "Sector #{name}"
            puts "#{@region.name}"
            puts
            if(@stations.length > 0) then
                puts "Station#{'s' if @stations.length>0}:
"+@stations.map{|s|s.name}.join(', ')
            end
            if(@planets.length > 0) then
                puts "Planet#{'s' if @planets.length>0}:
"+@planets.map{|p|p.name}.join(', ')
            end
            if(@links.length > 0) then
                puts "Nearby Sector#{'s' if @links.length>0}:
"+@links.map{|s|s.name}.join(', ')
            end

            puts
            puts '(D)ock with station' if @stations.length > 0
            puts '(L)and on planet' if @stations.length > 0
            puts '(W)arp to nearby sector' if @links.length > 0
            puts
            puts '(S)ave game'
            puts '(Q)uit game'
            print '?'

            response = gets.chomp
            case response
            when /^d/i
                @menu = :dock
            when /^l/i
                @menu = :land
            when /^w/i
                @menu = :warp
            when /^s/i
                @menu = :save
            when /^q/i
                @menu = :quit
            else
                puts " *** INVALID CHOICE ***"
                @menu = :main_menu
            end
        end

        def warp
            result = nil
            begin
                result = choose_move(@links, 'sector to warp to')
            end until result != :bad
            puts "Warping..." if result != :main_menu
        end

        def dock
            result = nil
            begin
                result = choose_move(@stations, 'station to dock with')
            end until result != :bad
            puts "Docking..." if result != :main_menu
        end

        def land
            result = nil
            begin
                result = choose_move(@planets, 'planet to land on')
            end until result != :bad
            puts "Landing..." if result != :main_menu
        end

        def choose_move choices, string
            if choices.length < 1 then
                puts "There is no #{string}"
                return @menu = :main_menu
            end
            puts "Choose #{string}:"
            puts
            choices.each_with_index do |c,index|
                puts "  #{index}: #{c.name}"
            end
            puts
            puts "(M)ain menu"
            print "?"

            response = gets.chomp
            choice = response.to_i
            if response =~ /^m/i then
                return @menu = :main_menu
            elsif choice.to_s != response || choice >= choices.length
then
                puts " *** INVALID CHOICE ***"
                return :bad
            else
                @player[:location] = choices[choice]
                return @menu = :done
            end
        end

        def save
            @menu = :main_menu
            filename = @player[:name]+".save"
            if(File.exists? filename) then
                puts "Do you want to overwrite previous saved game?"
                if gets.chomp !~ /^y/i
                    puts "Game not saved"
                    return
                end
            end
            File.open(filename, 'wb') do |f|
                Marshal.dump(@player, f)
            end
            puts "Game Saved."
        end

        def quit
            puts "Are you sure you want to quit? (y/n)"
            y_or_n = gets.chomp
            case y_or_n
            when /^y/i
                puts 'goodbye!'
                exit 0
            when /^n/i
                @menu = :main_menu
            else
                puts "Hmm... I'll assume you don't want to quit from
that."
                @menu = :main_menu
            end
        end
    end
end


if __FILE__ == $0
    Named = Struct.new(:name)
    region = Named.new('The Region')
    s = SpaceMerchant::Sector.new('Test Sector', region)
    5.times do |i|
        s.add_planet(Named.new("planet #{i}"))
        s.add_station(Named.new("station #{i}"))
        s.link(SpaceMerchant::Sector.new("#{i}", region))
    end

    player = {:name => "test"}
    s.handle_event(player)
    p player
end
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-20 09:48
(Received via mailing list)
Here is my Galaxy implementation. It seems to do everything it needs to
do, but I didn't go overboard with extra stuff since I don't know what
kind of stuff might be useful for the Galaxy to do...

The easiest way to use this with the GalaxyLoader seems to be to remove
the 'require galaxy' from GalaxyLoader and do it the other way around.
Then you can run the tests including the dump test with:

	ruby -rgalaxy_loader galaxy.rb

There are some naive benchmarks I used to make the obvious
optimizations, run them with:

	ruby galaxy.rb -bm

One additional thing I did consider was multiplayer - it's fairly easy
to get a galaxy working over DRb, and I did work up a basic multiplayer
version like this but had one overriding problem - all output ends up on
the server, or the game ends up useless because of constantly
marshalling sectors to send down the wire. I toyed with routing output
through the Player but still had problems... Ho hum.

Finally, I need to thank a couple of people: Dominik Bathon's solution
to Ruby Quiz 31 ( see
http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby... ) gave me
a much-needed start with Dijkstra's shortest path algorithm (in fact
it's only very slightly modified from his original) and Mauricio
Fernandez's WeakHash class let's me cache stuff irresponsibly (see
http://eigenclass.org/hiki.rb?weakhash+and+weakref )
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-20 11:13
(Received via mailing list)
On Mon, 2006-03-20 at 17:41 +0900, Ross Bamford wrote:
> Here is my Galaxy implementation.

I must've run these tests a few hundred times over the past few days,
and again before I posted - all green. Immediately after posting I'm
seeing failures roughly two fifths of the time because two or three
sectors are unreachable. Ain't it just typical?

I don't have time right now to fix it, but I'll have a look later on and
try to find out what's wrong.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-20 15:21
(Received via mailing list)
On Mar 20, 2006, at 2:41 AM, Ross Bamford wrote:

> One additional thing I did consider was multiplayer - it's fairly easy
> to get a galaxy working over DRb, and I did work up a basic
> multiplayer
> version like this but had one overriding problem - all output ends
> up on
> the server, or the game ends up useless because of constantly
> marshalling sectors to send down the wire. I toyed with routing output
> through the Player but still had problems... Ho hum.

This might be a fun follow-up quiz someday, if we can get the other
two pieces we need.  Anyone working on station.rb or planet.rb?

I'll see if I can steal a few hours tonight and knock one out...

James Edward Gray II
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-20 15:31
(Received via mailing list)
I'm working on a planet.rb.

Tim
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-20 17:36
(Received via mailing list)
Here is my improved sector.rb.  Yes, I know I said I wasn't going to
fix it up, but once Ross posted a working Galaxy implementation, I
couldn't resist testing it and fixing it up.  I fixed a few more bugs
and some problems it had due to a misunderstanding of Ross's galaxy
implementation.  One thing with this is that it no longer prepends
"Sector" in front of the sector name, since Ross's randomly generated
galaxy has sectors named "Sector 530" and so forth, and it looked
really stupid to have "Sector Sector 530."  The sample galaxy file
for the galaxy loader, on the other hand, just has the numbers, so if
used with that galaxy, it will just print "530" without any real
indication that 530 is a sector.  Still, I figured this was better
than "Sector Sector," and I didn't want to take the time to prepend
Sector if it wasn't all ready there.

Anyway, I'll be going ahead and putting together a planet.rb.
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-03-20 18:07
(Received via mailing list)
Ross Bamford wrote:
> One additional thing I did consider was multiplayer - it's fairly easy
> to get a galaxy working over DRb, and I did work up a basic multiplayer
> version like this but had one overriding problem - all output ends up on
> the server, or the game ends up useless because of constantly
> marshalling sectors to send down the wire. I toyed with routing output
> through the Player but still had problems... Ho hum.

What about including DRbUndumped in the sector class, to keep them on
the server?
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-20 18:10
(Received via mailing list)
James (or whoever else is doing station.rb), how are you representing
cargo and passengers, and where are you putting them in the player?
I was just starting to work on classes to represent items and people
that can be found and/or used on planets, and realized that there may
be a bit of overlap between items found on planets and bought at
stations, so they should at least be the same duck type.  I was
thinking that they'd be stored in player[:cargo] and player[:passengers]

Tim
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-20 18:26
(Received via mailing list)
On Mar 20, 2006, at 11:10 AM, Timothy Bennett wrote:

> James (or whoever else is doing station.rb), how are you
> representing cargo and passengers, and where are you putting them
> in the player?

I'm thinking I will introduce a Ship object in there.  I've cleared
some hours this evening to work on it and will post some details when
I get something going...

James Edward Gray II
280b41a88665fd8c699e83a9a25ef949?d=identicon&s=25 Stephen Waits (Guest)
on 2006-03-20 18:26
(Received via mailing list)
On Mar 20, 2006, at 8:35 AM, Timothy Bennett wrote:

> Here is my improved sector.rb.
>
> <sector.rb>

Would you guys like a public svn repository for this?  That way you
could all keep all of the various parts together.

If so, just say the word, I'll gladly host one for you..

--Steve
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2006-03-20 18:30
(Received via mailing list)
On 3/20/06, Timothy Bennett <timothy.s.bennett@gmail.com> wrote:
> James (or whoever else is doing station.rb), how are you representing
> cargo and passengers, and where are you putting them in the player?
> I was just starting to work on classes to represent items and people
> that can be found and/or used on planets, and realized that there may
> be a bit of overlap between items found on planets and bought at
> stations, so they should at least be the same duck type.  I was
> thinking that they'd be stored in player[:cargo] and player[:passengers]

I'm working on an implementation of Station and just representing
cargo items as symbols. Ie. the Station has a @trading hash keyed by
:machinery, :organics, etc. (whatever the world builder populates the
station with). When the player docks at the station, they see what's
available for what prices and are presented with a Buy/Sell/Exit
choice. When they, for instance, Buy, the Station will call two
methods on the Player: Player#pay(price) and Player#load_cargo(good,
quantity) where 'good' is the symbol representing that good. As such,
I don't need to know anything about the Player's representation, I
just require that Players visiting my Station provide the appropriate
methods. This representation of goods by symbols of course assumes
that a transported good has no state or operations beyond
classification.

Jacob Fugal
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-20 18:38
(Received via mailing list)
On Tue, 2006-03-21 at 01:35 +0900, Timothy Bennett wrote:
> One thing with this is that it no longer prepends
> "Sector" in front of the sector name, since Ross's randomly generated
> galaxy has sectors named "Sector 530" and so forth, and it looked
> really stupid to have "Sector Sector 530."  The sample galaxy file
> for the galaxy loader, on the other hand, just has the numbers, so if
> used with that galaxy, it will just print "530" without any real
> indication that 530 is a sector.  Still, I figured this was better
> than "Sector Sector," and I didn't want to take the time to prepend
> Sector if it wasn't all ready there.

Now you mention that, I think it's probably better if I remove that
automatic 'Sector' in the generated galaxies, assuming we're sticking
with the numbered sectors throughout. That way, the sector can decide on
how the number fits into it's name.
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-20 18:41
(Received via mailing list)
On Tue, 2006-03-21 at 02:07 +0900, Joel VanderWerf wrote:
>
The setup I had in the end was: (server) Galaxy as front object,
Sectors, Planets and Stations DRbUndumped so they stay on that side too,
with Player marshalled. I wanted to have Player unmarshalled too, but on
the client side, but I couldn't figure out how to make that work (I
wanted to keep that client side and route output through a method on
it).

Anyway, the problem with that was that, since Sector handles most of the
output (and will probably just call puts) the output all goes to the
server's terminal :(

I guess I just don't know DRb all that well yet...
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-20 18:48
(Received via mailing list)
On Mar 20, 2006, at 11:41 AM, Ross Bamford wrote:

> Anyway, the problem with that was that, since Sector handles most
> of the
> output (and will probably just call puts) the output all goes to the
> server's terminal :(

Could you have the client pass you $stdout and $stdin, then set them
on the server?

James Edward Gray II
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-20 22:00
(Received via mailing list)
On Tue, 2006-03-21 at 02:48 +0900, James Edward Gray II wrote:
> On Mar 20, 2006, at 11:41 AM, Ross Bamford wrote:
>
> > Anyway, the problem with that was that, since Sector handles most
> > of the
> > output (and will probably just call puts) the output all goes to the
> > server's terminal :(
>
> Could you have the client pass you $stdout and $stdin, then set them
> on the server?

I had a go with this, but couldn't get it to work. However, in the
process I figured out what I was doing wrong and got it working (I
think), so thanks :)

Two disclaimers: This is only basic, and untested, and I've not used DRb
until this so it's probably not as well put together as it might be...
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-21 00:35
(Received via mailing list)
On Mar 20, 2006, at 11:10 AM, Timothy Bennett wrote:

> I was thinking that they'd be stored in player[:cargo] and player
> [:passengers]

I haven't had time to introduce ships yet, so I went with pretty much
what you said.  Here's a very basic Station class to get this sucker
feeling more like a game:

#!/usr/local/bin/ruby -w

begin
   require "highline/import"
rescue LoadError
   begin
     require "rubygems"
     require "highline/import"
   rescue LoadError
     puts "#{__FILE__} requires HighLine be installed."
     exit 1
   end
end

class Numeric
   def commify
     to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/, '\1,').reverse
   end
end

module SpaceMerchant
   class Station
     Good  = Struct.new(:name, :cost)
     GOODS = [ Good.new(:plants, 0.5),
               Good.new(:animals, 0.8),
               Good.new(:food, 1),
               Good.new(:luxuries, 1.2),
               Good.new(:medicine, 2),
               Good.new(:technology, 3) ]

     def initialize( sector, name )
       @sector, @name = sector, name

       @goods = GOODS.sort_by { rand }[0..2].sort_by { |good|
good.cost }.
                      map { |good| good.dup }.
                      map { |g| [g, [:buy, :sell][rand(2)], rand
(10_000) + 1] }
       @goods.each { |good| good.first.cost *= rand + 0.5 }
     end

     attr_reader :sector, :name

     def handle_event( player )
       player[:cargo_space] ||= 20
       player[:cargo]       ||= Array.new

       puts "Welcome pilot.  Come to do some trading?  What'll it be?
\n\n"

       credits = player[:credits].commify.sub(/\.(\d+)$/) { |d| d
[0..2] }
       puts "Credits:  #{credits}"
       if player[:cargo].empty?
         puts "  Cargo:  none\n\n"
       else
         cargo = player[:cargo].map do |g|
           "#{g.first.to_s.capitalize} (#{g.last})"
         end.join(", ")
         puts "  Cargo:  #{cargo}\n\n"
       end

       choose do |menu|
         menu.index = :none
         menu.shell = true
         menu.case  = :capitalize

         menu.prompt = "Make an offer or blast off?  "

         printf "%10s %7s %5s %6s\n", "Item".center(10),
"Trade".center(7),
                                      "Price", "Amount"
         puts "---------- ------- ----- ------"
         @goods.each do |good|
           if good.include? :buy
             menu.choice( sprintf( "%-10s Buying   %1.2f",
                                   good.first.name.to_s.capitalize,
                                   good.first.cost ) ) do |good,
details|
               sell_goods(
                 player,
                 @goods.find { |g| g.first.name == good[/\w
+/].downcase.to_sym },
                 details.split
               )

               puts "You unload the goods and blast off from the
station..."
               player[:location] = sector
             end
           else
             menu.choice( sprintf( "%-10s Selling  %1.2f %6s",
                                   good.first.name.to_s.capitalize,
                                   good.first.cost,
                                   good.last.commify ) ) do |good,
details|
               buy_goods(
                 player,
                 @goods.find { |g| g.first.name == good[/\w
+/].downcase.to_sym },
                 details.split
               )

               puts "You load up the goods and blast off from the
station..."
               player[:location] = sector
             end
           end
         end

         menu.choice("Blast off") { player[:location] = sector }
       end
     end

     private

     def buy_goods( player, good, details )
       can_afford = [ good.last,
                      (player[:credits] * good.first.cost).to_i,
                      player[:cargo_space] -
                      player[:cargo].inject(0) { |sum, item|
item.last } ].min
       if can_afford == 0
         puts "I don't think you are in any position to be buyin'."
         return
       end

       amount = if details.first.nil? or details.first.to_i > can_afford
         ask("How much?  ", Integer) { |q| q.in = (1..can_afford) }
       else
         details.shift.to_i
       end

       player[:credits] -= good.first.cost * amount
       if add_on = player[:cargo].find { |g| g.first ==
good.first.name }
         add_on[-1] += amount
       else
         player[:cargo] << [good.first.name, amount]
       end

       reset_good(good, amount)
     end

     def sell_goods( player, good, details )
       begin
         max_sale = player[:cargo].find { |g| g.first ==
good.first.name }.last
       rescue
         puts "Uh, you don't have any of that to sell Mac."
         return
       end

       amount = if details.first.nil? or details.first.to_i > max_sale
         ask("How much?  ", Integer) { |q| q.in = (1..max_sale) }
       else
         details.shift.to_i
       end

       player[:credits] += good.first.cost * amount
       player[:cargo].find { |g| g.first == good.first.name }[-1] -=
amount

       reset_good(good, amount)
     end

     def reset_good( good, amount )
       if (good[-1] -= amount) <= 0
         good[1..2] = [([:buy, :sell] - [good[1]]).first, rand
(10_000) + 1]
       end
     end
   end
end

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

__END__

James Edward Gray II
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-21 00:45
(Received via mailing list)
On Mar 20, 2006, at 3:00 PM, Ross Bamford wrote:

>
> I had a go with this, but couldn't get it to work.

Bummer.  Here is where I got the idea, just FYI:

http://ian.blenke.com/drb

James Edward Gray II
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-21 01:30
(Received via mailing list)
On Mar 20, 2006, at 3:34 PM, James Edward Gray II wrote:

> On Mar 20, 2006, at 11:10 AM, Timothy Bennett wrote:
>
>> I was thinking that they'd be stored in player[:cargo] and player
>> [:passengers]
>
> I haven't had time to introduce ships yet, so I went with pretty
> much what you said.
>

Ok, great, I'll work with that then.

Tim
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-21 05:18
(Received via mailing list)
Ok, here's my initial planet.rb.  I had meant to put people on the
planet, but I didn't finish that.  I did, however, set up a mechanism
for finding items which can later be used (a block is attached to the
item's .new, which determines the item's effect).  This is incomplete
still (only really works if there's only one item of each type), but
I wanted to post this since it should still be fun for you guys to
see.  Hopefully I'll have time to add some more to this tomorrow.

By the way, the test (activated by running planet.rb by itself - but
it still depends on Sector), includes an item Omega.  Explore for a
while (chances of finding it are about 1 in 10), and use it :)

Later, also, if people are interested, we can extend Jacob's
GalaxyLoader to include item definitions.

Oh, and I also added a description attribute for the planet, which
should probably also be incorporated into GalaxyLoader.

Tim
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-21 09:56
(Received via mailing list)
On Tue, 2006-03-21 at 08:45 +0900, James Edward Gray II wrote:
> >> Could you have the client pass you $stdout and $stdin, then set them
> >> on the server?
> >
> > I had a go with this, but couldn't get it to work.
>
> Bummer.  Here is where I got the idea, just FYI:
>
> http://ian.blenke.com/drb

Ahh, I see. It looks like we had pretty similar concepts of how it
should work, but my stdout/stdin references kept on turning into
DRb::DRbUnknown instances after I made the first call on them. The other
problem was that, because we have to handle multiple players and sectors
use 'puts' for output, I had to keep the right bits for each player
somewhere and redefine puts to take account of that. Fortuntely DRb
seems to do thread-per-client so I could store it there.

Doing that, though, I realised it might be nicer to route everything
through the player after all - the output setup is currently completely
transparent from the game classes' point of view, so could be easily
changed e.g. to use a different output style, or for
internationalization (instead of marshalling the messages, give each
message a number that is sent over the wire, and looked up on the client
to get the right message).

Also, since the player is provided by the client, different clients
could be using different output formats or whatever with the server
having no knowledge of that :)
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-21 15:47
(Received via mailing list)
On Tue, 2006-03-21 at 17:56 +0900, Ross Bamford wrote:
> > >>
> should work, but my stdout/stdin references kept on turning into
> DRb::DRbUnknown instances after I made the first call on them.
>

Okay, I have this working now after a fashion, but still can't get it to
work with the station implementation - I don't know highline well, but
looking through it seems to use readline, termios, or something else on
Windows and I'm not sure we can do anything about that...

So currently it can play multiplayer, as long as you don't dock with a
station (which causes the server to wait for input!) :-(

Have to say, though, that playing in one-player is actually pretty good,
it's coming together well guys :) I think I need to make some
adjustments to the galaxy generation, not just because it sometimes
generates those galaxies with a couple of interlinked, unreachable
sectors, but also because it feels a bit sparse in terms of planets and
stations...
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-21 15:56
(Received via mailing list)
On Tue, 2006-03-21 at 13:18 +0900, Timothy Bennett wrote:

> Later, also, if people are interested, we can extend Jacob's
> GalaxyLoader to include item definitions.
>
> Oh, and I also added a description attribute for the planet, which
> should probably also be incorporated into GalaxyLoader.

Maybe we could work it with a block on planet/station initialize, so
that in the .glx file it'd look like:

  planet("HUY38M") do
    description = "whatever"
    something = "whichever"
  end

(GalaxyLoader planet and GalaxyLoader station would need to pass the
block, of course)

To make it work both ways, I think we'd need to extend the current
Galaxy#dump to call through to a new method, #dump, on each planet and
station (may as well do sector to, and have the whole dump handled this
way?). This method is responsible for outputting the ruby code above
at dump time. Thus, planets/stations can support whatever they like by
generating as they need to.

?
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-21 18:24
(Received via mailing list)
On Mar 21, 2006, at 2:56 AM, Ross Bamford wrote:

> Also, since the player is provided by the client, different clients
> could be using different output formats or whatever with the server
> having no knowledge of that :)

This is a great point.  Good ideas all around.  I believe you're
right that this would be the correct way to handle multi-player.

James Edward Gray II
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-21 18:25
(Received via mailing list)
On Mar 21, 2006, at 8:47 AM, Ross Bamford wrote:

> Have to say, though, that playing in one-player is actually pretty
> good,
> it's coming together well guys :)

Yeah, this was a fun little experiment.  I'm glad we were able to get
it going.

James Edward Gray II
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 Mauricio Fernandez (Guest)
on 2006-03-22 16:22
(Received via mailing list)
On Mon, Mar 20, 2006 at 05:41:56PM +0900, Ross Bamford wrote:
>     # This WeakHash implementation is by Mauricio Fernandez. I
>     # just added support for a default, and full key reclaimation
>     # to suit our need to have the default block run as and when
>     # (but only when) necessary.
>     #
>     # See: http://eigenclass.org/hiki.rb?weakhash+and+weakref

I found your message indirectly via my httpd logs :)

>     class WeakHash #:nodoc: all
[...]
>       def []( key )
>         value_id = @cache[key.hash]
                            ========
This will fail when you have hash collisions; I think it should be 'key'
as in the original code (same goes for []=).

Forcing collisions is not as difficult as one could believe:


#####
  class WeakHash #:nodoc: all
    attr_reader :cache
    def initialize( cache = Hash.new, &initializer )
      @cache = cache
      @initializer = initializer
      @key_map = {}
      @rev_cache = Hash.new{|h,k| h[k] = {}}
      @reclaim_value = lambda do |value_id|
	if @rev_cache.has_key? value_id
	  @rev_cache[value_id].each_key{|key| @cache.delete key}
	  @rev_cache.delete value_id
	end
      end
      @reclaim_key = lambda do |key_id|
	if @key_map.has_key? key_id
	  @cache.delete @key_map[key_id]
	  @key_map.delete key_id
	end
      end
    end

    def []( key )
      value_id = @cache[key.hash]

      if value_id.nil? && @initializer
	self[key] = @initializer.call(self, key)
	value_id = @cache[key.hash]
      end

      return ObjectSpace._id2ref(value_id) unless value_id.nil?
      nil
    end

    def []=( key, value )
      case key
      when Fixnum, Symbol, true, false
	key2 = key
      else
	key2 = key.hash
      end
      @rev_cache[value.object_id][key2] = true
      @cache[key2] = value.object_id
      @key_map[key.object_id] = key2

      ObjectSpace.define_finalizer(value, @reclaim_value)
      ObjectSpace.define_finalizer(key, @reclaim_key)
    end
  end

RUBY_VERSION                                       # => "1.8.4"
hash = WeakHash.new
hash[["a"]] = "some value"
hash[["a"]]                                        # => "some value"
hash["c"]                                          # => "some value"
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-22 17:08
(Received via mailing list)
On Thu, 2006-03-23 at 00:22 +0900, Mauricio Fernandez wrote:
> On Mon, Mar 20, 2006 at 05:41:56PM +0900, Ross Bamford wrote:
> >     # This WeakHash implementation is by Mauricio Fernandez. I
> >     # just added support for a default, and full key reclaimation
> >     # to suit our need to have the default block run as and when
> >     # (but only when) necessary.
> >     #
> >     # See: http://eigenclass.org/hiki.rb?weakhash+and+weakref
>
> I found your message indirectly via my httpd logs :)
>

I meant to put a thanks in the message body after I decided the code was
too long to post inline, but I forgot (sorry about that). Glad you saw
it, and thanks for the wicked implementation :)

> >     class WeakHash #:nodoc: all
> [...]
> >       def []( key )
> >         value_id = @cache[key.hash]
>                             ========
> This will fail when you have hash collisions; I think it should be 'key'
> as in the original code (same goes for []=).

Ahh, of course. I think collisions are less likely given the specific
usage of WeakHash in this case (arrays with strictly object_id based
hashes, of objects that outlive the hash), but of course still possible
so I'll fix that up later today.

Cheers,
A9b6a93b860020caf9d2d1d58c32478f?d=identicon&s=25 Ross Bamford (Guest)
on 2006-03-22 20:58
(Received via mailing list)
On Thu, 2006-03-23 at 01:08 +0900, Ross Bamford wrote:
> [...] I'll fix that up later today.
For completeness, here is the final galaxy.rb with this bug fixed. I
still didn't get time to redo the generation though but there you go...
28d78f0eb6df0191896f8696741b63d5?d=identicon&s=25 Gary Watson (pfharlock)
on 2006-03-22 22:18
This is awsome.  I've been thinking about writing a tradewars server for
a month or so now.  Unfortunately with school and everything else, I
haven't had much time to devote to the idea.  I hope people come up with
neat solutions.  I wish I had the time to devote to this quiz, (I don't
unfortunately), but if this quiz results in a project being spun off, I
would love to become a part of it.

My original idea was to try to write a multi-threaded telnet server in
ruby to power the game, and allow players to play the game
simultaneously, allowing for in game chat and ship to ship
communications and whatnot.  (much like the latest version of tradewars
2002 game server from Epic Interactive www.eisonline.com/twgs/.

At any rate, good luck to everyone, I'm definately interested in the
results.
801b207647d808477fc4514568c9d723?d=identicon&s=25 Dimitri Aivaliotis (Guest)
on 2006-03-23 06:43
(Received via mailing list)
I know this is 11th-hour and all, but I thought I'd mention it
anyways.  When I read this quiz, I immediately thought of using a
Dependency Injection framework to solve it.  I also wanted to add
elements of the Firefly `Verse to the gameplay, so I'd describe it in
a YAML file.  Unfortunately, I haven't had the time to do more than
just a mock-up:

  registry = Needle::Registry.new do |reg|

    reg.register( :galaxy ) { Galaxy.new( 'firefly.yaml' ) }
    reg.intercept( :galaxy ).with! { logging_interceptor }
    reg.register( :sector, :model => :multiton ) do |c,p,name,location|
      Sector.new( name, location )
    end
    reg.intercept( :sector ).with! { logging_interceptor }
    reg.register( :planet, :model => :multiton ) do |c,p,name,sector|
      Planet.new( name, sector )
    end
    reg.intercept( :planet ).with! { logging_interceptor }
    reg.register( :station, :model => :multiton ) do |c,p,name,sector|
      Station.new( name, sector )
    end
    reg.intercept( :station ).with! { logging_interceptor }
    reg.register( :player, :model => :multiton ) do |c,p,name|
      Player.new( name )
    end
    reg.intercept( :player ).with! { logging_interceptor }
  end

  galaxy = registry.galaxy

firefly.yaml (partial):

---
galaxy: firefly
player:
  credits    : 1000
  reputation : 100
sectors:
  - name     : 1
    location : central
    planets:
      - name        : Ariel
        description : "Planet.  Home to a major medical center, Saint
Lucy's."
  - name     : 2
    location : central
    planets:
      - name        : Persephone
        description : Planet.  Heavily stratified societal structure.
Resupply port for Serenity.

I had envisioned this as a webapp, where players could register and
all play within the same universe.  The Authenticator and Presenter
aren't shown above.  Each player would then be instatiated like so:

<username> = registry.player("<username>")

They could save the game state to a YAML file, which would then be
transportable across SpaceMerchant implementations.

So, what do you all think?  Anybody with actual DI/IoC experience care
to chime in?  (As you can probably tell, this would be my first time
using DI.)  Are there any other Browncoats out there?  Could this
maybe be a Part 2 to this quiz?

- Dimitri
24d3102d656a4654db23d28382a2d6f0?d=identicon&s=25 Timothy Bennett (Guest)
on 2006-03-23 07:05
(Received via mailing list)
On Mar 22, 2006, at 1:18 PM, Gary Watson wrote:

>
> My original idea was to try to write a multi-threaded telnet server in
> ruby to power the game, and allow players to play the game
> simultaneously, allowing for in game chat and ship to ship
> communications and whatnot.  (much like the latest version of
> tradewars
> 2002 game server from Epic Interactive www.eisonline.com/twgs/.
>
> At any rate, good luck to everyone, I'm definately interested in the
> results.

On Mar 22, 2006, at 9:43 PM, Dimitri Aivaliotis wrote:

> maybe be a Part 2 to this quiz?
I dunno about continuing with this as a quiz, but I think it'd be
cool to see a project spin off of this.  Anyone want to take the lead
and set up a project on RubyForge?  I personally won't have a great
deal of time to put into this (and I'm still rather wet behind the
ears as a Ruby programmer), but I still wouldn't mind setting it up.

Tim
12a71a456ac3d464914a8267f11d43b3?d=identicon&s=25 semmons99@gmail.com (Guest)
on 2006-03-23 13:33
(Received via mailing list)
I would be willing to help. I have been thinking for months on a way to
implement a game in the verse. Let me know what's going on, until then
I will just drift out here in the black.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-03-23 14:56
(Received via mailing list)
On Mar 22, 2006, at 11:43 PM, Dimitri Aivaliotis wrote:

> I had envisioned this as a webapp, where players could register and
> all play within the same universe.

I *almost* made the quiz like this, but changed my mind to keep
things a little easier.  It's a good idea.

James Edward Gray II
This topic is locked and can not be replied to.