TeensyMud - 'A ruby mud server.'

Subject: Setting up constants
Subject: Setting up constants
Author: Massaria
Posted: 01/09/2006 11:28AM

I'm writing up the code that will check if a player evolve. Currently, this is a matter of matching the player's @genome to the template genome of a given type of animal.
I could have each player carry around these match-constants and check against those, but I'll end up with a dosen arrays and hashes, or more, so it doesn't seem to be the clever way of doing it.

I've considered putting them up in a YAML file, but loading that file every time a gene is added will hog resources unnecessarily.

In short, how do I put up a single instance of, for example, an array, that is easily accessible at other points in the code?

Thanks,
Mass

reply
Subject: Setting up constants
Author: Test
Posted: 01/09/2006 01:22PM

Massaria wrote:
>
> I've considered putting them up in a YAML file, but loading that file every time a gene is added will hog resources unnecessarily.
>
> In short, how do I put up a single instance of, for example, an array, that is easily accessible at other points in the code?

Well the simplest way is to declare a global variable.
$gene_pool = []

Globals really aren't the best way to write programs (see the TeensyMud use of $engine and $world *shiver*). A better way might be to use ruby's Singleton to build a singleton class, which is only instanciated once.
require 'singleton'
class GenePool
  include Singleton

  def initialize
    @genes = []
  end
  
  # returns the match or nil if no match
  def match(gene)
    @genes.find {|g| g == gene}
  end
end  

Then anywhere in your code you can access the singleton GenePool? object with...
  gp = GenePool.instance
  if !gp.match(my_genes) 
    puts "You are a mutant freak"
  end


The nice thing about the above is it wraps the array so you can control all the ways it can be used. You can only get a handle to it with instance and run match against it.

Now the initialize method is only run once for the GenePool? class above no matter how many times you call instance. So with that in mind you could load the array from a file or yaml in initialize. Then declare a save method on GenPool? that saves it which you could call whenever you feel like saving.

Tyche aka Test (:


reply
Subject: Setting up constants
Author: Massaria
Posted: 01/11/2006 11:50AM

> Well the simplest way is to declare a global variable.
>
> $gene_pool = []
> 

> Globals really aren't the best way to write programs (see the TeensyMud use of $engine and $world *shiver*). A better way might be to use ruby's Singleton to build a singleton class, which is only instanciated once.
...
> The nice thing about the above is it wraps the array so you can control all the ways it can be used. You can only get a handle to it with instance and run match against it.
>

I've gone with the simple '$gene_pool = []' way, for now. Mostly because they're always the same and doesn't change from boot-up to boot-up.

$animalia_genes = [whatever]
$radiata_genes = [more whatever]


It worked perfectly until i needed to compare the @taxon variable of the player to these constants. I simply wanted to somehow extract all the constants listed in my player.rb file and match them to this taxon variable, but I'll be damned if I can find them.
I've tried various permutations of Mod.constants and Mod.ancestors and a few other things, but when ruby started dumping its core and such, well, I thought it was time to come here ;-)

Thanks
Mass.

reply
Subject: Setting up constants
Author: Tyche
Posted: 01/12/2006 05:10PM

Massaria wrote:
>
  $animalia_genes = [whatever]
  $radiata_genes = [more whatever]
  

...
> I've tried various permutations of Mod.constants and Mod.ancestors and a few other things, but when ruby started dumping its core and such, well, I thought it was time to come here ;-)

Ah... okay constants. I knew the thread was titled that for a reason. I don't know why I was thinking globals. Maybe because you want to access them everywhere. Constants are upper cased and you'll get warnings if you try to assign to them more than once. They are put in the scope of the class or module where they are declared. So you can put them in a module.

module Genes
  ANIMAL_GENES = [whatever]
  RADIATE_GENES = [more whatever]
end


Then when you want to access them you use Genes::ANIMAL_GENES, assuming you required the module above. Or to get an array of the constants do Genes.constants. The names in the array will be strings. To destringify them you'd do Genes.const_get("ANIMAL_GENES").






reply
Subject: Setting up constants
Author: Massaria
Posted: 01/13/2006 12:23PM

Damn it. I really, truely, intensy hate coming here again with this, since I know you've patiently tried to explain it to me before. I just can't figure out why these objects are so incredibly dumb.

Ok. So I've set up a Genes class in a new genes.rb file:
class Genes

  attr_accessor :seq, :taxon, :chrom, :title
  
  POOL = YAML::load_file("genes.yaml")
  
  def initialize
    @seq = ""
    @taxon = ""
    @chrom = ""
    @title = ""
  end

  def to_s
    @chrom
  end
  
  # var = a hash or array on player containing chroms 
  # chrom = the chrom to add to hash
  def Genes.add_new_chrom(var, chrom)
    if var.class == Hash
      var.store(chrom, [])
    else
      var.push(chrom)
    end
  end
  
  def Genes.do_tmp
    glist =  Genes.const_get("POOL")

    #Genes::POOL.each {|g| glist.push(g) }
    glist.each { |x| sendto("x= #{x.inspect}") }
  end
  
end #class genes


I'm calling Genes.do_tmp from the tmp_cmd:
def cmd_tmp(args)
    Genes.do_tmp
end


But no matter where I place the various commands, no matter if I start redifining methods and no matter that it knows perfectly well that the object are class:Genes - it just can't find FX 'seq' or pretty much anything.
It was the same problem I was battling in the Singleton business you showed me for the hour that went before the 3 I just used on this idiocy.
I know all the stuff is in there somewhere, as I did succeed in 'inspecting' my various constellations a few times, but that's about the extend of it.
Usually I get back things like:
undefined method `has_key?' for # (NoMethodError?)
undefined local variable or method `seq' for Genes:Class (NameError?)
or my fav, from the above example:
undefined method `sendto' for Genes:Class (NoMethodError?)

I know it's stupid. sigh.
Mass.




reply
Subject: Setting up constants
Author: Tyche
Posted: 01/13/2006 10:49PM

I cannot quite figure out what you are trying to do. It looks to me like the Genes class is serving many purposes. I really don't understand any of the relationships between the various things; taxons, genes, gene, genomes, chromosomes or the POOL. I thought chromosomes contained genes, and it looks odd that a class named Genes contains chromosomes. I'm totally clueless about the subject matter though.

I do know that if you are defining all sorts of class level methods then it's likely something is wrong.


reply
Subject: Setting up constants
Author: Massaria
Posted: 01/14/2006 07:57AM

Tyche wrote:
> I cannot quite figure out what you are trying to do.

Yeah, I guess it does lack a real question or two. sorry.

>It looks to me like the Genes class is serving many purposes.

It does, at least for now. Is that a big no-no?

>I really don't understand any of the relationships between the various things; taxons, genes, gene, genomes, chromosomes or the POOL. I thought chromosomes contained genes, and it looks odd that a class named Genes contains chromosomes. I'm totally clueless about the subject matter though.

The POOL constant is meant to hold all the genes.
The genes are stored in a YAML file a la:

---
- !ruby/object:Gene
seq: A
taxon: animalia
chrom: symmetry
title: Makes symmetry Radial
- !ruby/object:Gene
seq: B
taxon: animalia
chrom: symmetry
title: Makes symmetry Bilateral
...

I use this list to look up which genes are accessible to the player at various points in his evolution, and this is why I have 'chromosome' and 'taxon' in each gene. Each player will get an updated set of chromosomes each time they evolve and go down a branch of the tree, and when they want to add more genes, a list is generated that contains all the genes that match the current taxon and chromosomes of the player.
A chromosome is not an actualy attribute as such, it's just a way of grouping the genes.
The @genome attribute of the player hold a hash with each key being the name of a chromosome (whether active/open or not), and the value is an array of all the genes the player has 'incorporated' into that chromosome.

I actually had all of the above working (in the Player class), but it required that the yaml file was loaded and checked through every time a player wanted to list which genes he could chose from - and that doesn't seem like a very resource-friendly way to handle it. Thus I wanted to set up that POOL constant and check against that when a player asked for a gene list.
Of course, this POOL list will eventually hold about 200 genes, each with 4-5 hash-pairs - so maybe that's not the best way either?

In any case, the trouble I'm having is with accessing the list once it's made.
I see that it contains all the info I need, but I can't figure out how to access it (vis-a-vis the errors I posted)

*strokes his VooDoo? Tyche*
"Don't fail me now..."

Mass.

_______________________
EDIT:

Dangit. This problem is at least in part due to a blunder of mythic proportions, even by my standards.

I renamed the class from Gene to Genes, without changing the entries in the yaml file. Trying to fetch 'seq' from a 'Genes' class that really is a 'Gene' class - sigh. Oh well, I wont do that again any time soon.

I still have a probelm though, with the interaction of these classes. I can't use 'sendto' from my Gene class/file, probably other methods too.
Do I really need to put up each method I need in every new class I make?
I doubt it, but what am I mising?
requiring and Including Publisher doesn't seem to help any.

Thanks
Mass.

reply
Subject: Setting up constants
Author: Tyche
Posted: 01/14/2006 10:47AM

Okay hmm how about the above using class level variables?
require "yaml"

class Gene
  attr_accessor :seq, :taxon, :chrom, :title

  def initialize
    @seq = ""
    @taxon = ""
    @chrom = ""
    @title = ""
  end

  def to_s
    @chrom
  end

end

class GenePool
  @@pool = YAML::load_file("genes.yaml")

  def self.inspect_all
    @@pool.inject(str="") {|str, x| str + "x= #{x.inspect}\n"}
  end

  def self.each(&blk)
    @@pool.each &blk
  end

end

puts GenePool.inspect_all

GenePool.each {|gene| puts "#{gene}"}
Outputs:
x= #Gene:0x2dbfdb0 @chrom="symmetry", @taxon="animalia", @seq="A", @title="Makes symmetry Radial">
x= #Gene:0x2dbf798 @chrom="symmetry", @taxon="animalia", @seq="B", @title="Makes symmetry Bilateral">
symmetry
symmetry


You probably don't want to ever initialize an instance of GenePool? (new) or inherit it. You cannot access @@pool outside GenePool?.

I changed it so it builds a report string and returns it (a one liner) as you can't call Player#sendto without an instance of Player. And I added an iterator GenePool?#each that just passes the call to @@pool.

I don't know what the other routine add_chrom is supposed to do. I don't think you want to merge these things into a single as it only causes confusion between a singleton class that acts as container and a class that describes the behavior of its instances.

There's not much difference semanticly between the above, which uses the class itslef as a singleton object, and the Singleton class I made earlier which creates a single object instance.





reply
Subject: Setting up constants
Author: Tyche
Posted: 01/14/2006 11:30AM

Massaria wrote:
> I still have a probelm though, with the interaction of these classes. I can't use 'sendto' from my Gene class/file, probably other methods too.
> Do I really need to put up each method I need in every new class I make?
> I doubt it, but what am I mising?

sendto is bound to an instance of Player. You need a player as a receiver.

All the comamnds... cmd_who, cmd_look are included as instance methods on Player. So when you are executing the code in cmd_look, self points to the player.
module Cmd
  # shows a list of all connected players
  def cmd_who(args)
    $engine.world.players_connected.each {|p| sendto(p.name)}
  end
end
sendto above has an implicit receiver of self.
Explicitly...the line could be rewritten:
$engine.world.players_connected.each {|p| self.sendto(p.name)}
If you want to use sendto outside of player, you need to pass whatever routine you write a player.
class Foo
  def message(plyr, msg)
    plyr.sendto(msg)
  end
end


reply
Subject: Setting up constants
Author: Massaria
Posted: 01/14/2006 11:31AM


I'm sorry to have wasted your time with this blunder. If it's any consolation, I like to think that I understand objects and instances a little better now.

> I changed it so it builds a report string and returns it (a one liner) as you can't call Player#sendto without an instance of Player.

My understnading isn't good enough to figure out how to get that instance of player - I really do need it. Many of the player attributes will be used and modified in the Gene class, so I need to have a handle to FX @taxon and @genome and others.

So, in no uncertain terms: How do I include the player's instance variables in my Gene Class?

Thanks Tyche, you really are an incredible patient man.

reply
Subject: Setting up constants
Author: Tyche
Posted: 01/14/2006 12:52PM

Massaria wrote:
>
> So, in no uncertain terms: How do I include the player's instance variables in my Gene Class?

Are you trying to model Players have many genes backwards as Gene belongs to many Players?

GenePool?
all genes

Player
knows nothing

If so it appears to me that a Gene should be defined as:
@chrom="symmetry"
@taxon="animalia"
@seq="A"
@title="Makes symmetry Radial"
@players_that_have_this_gene = ["Bubba", "Buffy", etc. ]

In this case a player knows nothing about their genes. A player would ask GenePool? for their genes.


If instead you are modeling Player has many genes

GenePool?
all genes

Player
set of genes

Gene
as you have it defined

In this case a gene knows nothing about their Player they belong to. A player knows what Genes they have. You should never have need to access player in Gene. It makes no sense.

Or are you trying to do both a multiway mapping of Player has many genes and genes have many players? Confused.




reply
Subject: Setting up constants
Author: Massaria
Posted: 01/14/2006 01:21PM

> If instead you are modeling Player has many genes

Right, this is what I'm trying to do. The @genome attribute on the player will hold all the genes and chromosomes the player has. Perhaps the other way is better, as it seems to take up less memory. hmm.

> In this case a gene knows nothing about their Player they belong to. A player knows what Genes they have. You should never have need to access player in Gene. It makes no sense.
>

No, I guess it doesn't. I could have all the 'player-altering' stuff in class:Player, and the methods that do stuff with genes in Class:genes - seems reasonable enough when you think about it ;-)
Heh, it takes a little reajustment getting used to the ways of OO programming.


> Or are you trying to do both a multiway mapping of Player has many genes and genes have many players? Confused.

I guess I was :-(

I think I may just be able to move a bit further now.
Thanks a million Tyche!
Huggies
Mass.

reply