Class and inheritance

Hi All,

I am not a programmer, yet. I am trying ruby on for size, most things
fit ok, but Classes are puzzling me.

class Roll def Roll(base,range) @base=base @range=range @roll = base+random(range) end end

class Stats
def power
@power=Roll.new(10,8)
end
def speed
@speed=Roll.new(10,8)
end
def smarts
@smarts=Roll.new(10,8)
end
end

class Attribs < Stats
def acc
@acc=0.75self.power+(self.speed/self.smarts)
end
def health
@health = self.power
(self.smarts/self.speed)
end
end
class Npc < Attribs
def name(name)
@name=name
end
end

joe=Npc.new
joe.name(‘Joe’)
#puts joe.name.to_s+’ = name’
puts joe.power.to_s+’ = power’
puts joe.smarts.to_s+’ = smarts’
puts joe.speed.to_s+’ = speed’
puts joe.health.to_s+’ = health’
puts joe.acc+’ = acc’
puts joe
puts ‘=======================’
puts ‘Calculations’
puts 0.75*joe.power+(joe.speed/joe.smarts)

OK, so why is this broken?

Le 03 mars à 16:03, August0866 a écrit :

Hi.

I am not a programmer, yet. I am trying ruby on for size, most things
fit ok, but Classes are puzzling me.

class Roll def Roll(base,range)
 @power=Roll.new(10,8)

OK, so why is this broken?

The constructor in ruby is named initialize, not the name of the class
like in Java. Try :

class Roll
def initialize(base, range)

end

end

Fred

On Monday 03 March 2008, August0866 wrote:

      @roll = base+random(range)
def smarts

end
puts joe.power.to_s+’ = power’
OK, so why is this broken?
There are many reasons your code doesn’t work. Let’s see what happens
running
it in ruby (by the way, when asking for help about a piece of code which
produces an error, it’s better to also show the error message):

  • FIRST TRY:
    ./prova.rb:13:in `initialize’: wrong number of arguments (2 for 0)
    (ArgumentError)

This means that some initialize method is called with 2 arguments, when
it
should receive no argument on line 11. The line in question is:

@power = Roll.new(10, 8)

Apparently, there’s no call to initialize here. What you’re trying to do
in
this line, is to create an instance of class Roll, calling Roll.new. For
most
classes, the ‘new’ method first creates a new object, then calls its
‘initialize’ method, which, as the name implies, is used to perform
initialization on the object (for example, giving starting values to
instance
variables). Since you didn’t define an initialize method for class Roll,
it
uses the one defined for the parent class, which is Object.
The initialize method defined for class Object takes no argument, so you
get
an error.

Looking at the definition of class Roll, i see you defined a method
called
Roll, which takes two arguments and sets instance variables, just like
initialize should do. I guess you thought that method would have been
called
by new. Let’s rename it to initialize and see if it solves the problem.

  • SECOND TRY:
    ./prova.rb:5:in initialize': undefined methodrandom’ for
    #<Roll:0xb7c10c70
    @range=8, @base=10> (NoMethodError)

This is easy: you’re calling a method named ‘random’, which doesn’t
exist.
The method which generates a random number in ruby is called rand, so
we’ll
replace random with rand

  • THIRD TRY:
    ./prova.rb:26:in health': undefined method/’ for #<Roll:0xb7beeb34
    @range=8, @base=10, @roll=15> (NoMethodError)
    from ./prova.rb:41

Now, ruby hits an error while calling the health method: in particular,
in
line 26 there’s a call to a method ‘/’ which is undefined. Line 26 is:

@health = self.power*(self.smarts/self.speed)

In ruby, operators such as +, -, * and / are simply methods, so that
when you
write, for example 2 + 3, ruby calls the ‘+’ method of the object 2
passing
it the object 3 as argument. In your code, you’re dividing self.smarts
by
self.speed, which are both of class Roll. Class Roll doesn’t have a
method
called ‘/’, so you get the error.

This problem could be solved defining a sensible / method for class
Roll, or,
more simply, by defining a method which allows to get the value of the
variable @roll of an instance of roll (which is a number), and use that
instead of the instance of Roll itself. It may be worth asking whether
class
Roll really needs to exist. If you won’t require the @base and @range
values
somewhere else, then it would be much easier to replace the whole class
with
a method such as this:

def roll(base, range)
base + rand(range)
end

At any rate, let’s assume that Roll needs to be a class and add a method
which allows to extract the @roll instance variable. We could define it
by
hand, but ruby does it automatically if we add this line inside class
Roll:

attr_reader :roll

This generates a method called roll which returns @roll.

Then, we need to replace speed and smarts and power with speed.roll,
smarts.roll and power.roll in lines 24, 26 and 47 (we would get an
analogous
problem with the acc method if we leaved it as it is).

Before looking at the error, there are two things to notice: first,
power,
smarts and speed aren’t numbers but instances of class Roll. If this
isn’t
what you expected, then most likely you should replace class Roll with
the
method roll as I mentioned above. Second, health is 0 (of course, this
will
depend on the random number which have been generated). Since both
smarts.roll and speed.roll are integers, the division will return 0 if
the
divider is greater than the divisor. To avoid this, add a call to to_f
to one
of the operands. to_f will convert the integer to a float, so the
division
will use decimals.

Looking at the error, it simply means that, in line 43, we’re trying to
add a
number with a string. The reason is simply that you forget to call the
to_s
method on joe.acc as you did for the other ones.

Now, it works.

Attached, you’ll find a copy of the code with all the modifications I
spoke
about.

I hope this helps

Stefano

On Mar 3, 10:15 am, “F. Senault” [email protected] wrote:

 @power=Roll.new(10,8)

end

Fred

ok, new attempt

class Roll
def initialize(base, range)
@roll = base+rand(range)
return @roll
end
end

class Stats
def power
@power=Roll.new(10,8)
end

def speed
  @speed=Roll.new(10,8)
end

def smarts
 @smarts=Roll.new(10,8)
end

end

class Attribs < Stats
def acc
@acc=self.power+(self.speed / self.smarts)# doesnt want to do
the math ?Why?
end

def  health
  @health = 0.00+self.power*(self.smarts/self.speed)# doesnt want

to do the math ?Why?
end
end
class Npc < Attribs
def initialize(name)
@name=name
end
def job
puts ‘stuff’
end
end

class Test
def test
a=Roll.new(10,8)
b=Stats.new
c=Attribs.new
d=Npc.new(‘Joe’)

puts '++++++++++++++++A+++++++++++++++++++'
puts 'a object is '+a.to_s
puts 'a\'s method is called roll '+a.inspect
puts '++++++++++++++++A+++++++++++++++++++'
puts '================B==================='
puts 'b is object '+b.inspect.to_s
puts 'b methods are power = '+b.power.to_s
puts ', speed ='+b.speed.to_s
puts ', and finaly smarts = '+b.smarts.to_s
puts '================B=================='
puts '________________C__________________'
puts 'now on to c'
puts c.to_s+' is the object name for c'
puts 'c has all the methods of a and b plus its own'
puts ' c has a power of '+c.power.inspect.to_s
puts ' c has a speed of '+c.speed.inspect.to_s
puts ' c has a smarts of '+c.smarts.inspect.to_s
puts ' plus c has an Accuracy of '+c.acc.to_s
puts ' plus c has a health of '+c.health.to_s
puts '________________C____________________'
puts '||||||||||||||||D||||||||||||||||||||'

puts 'D is the goal'
puts d.inspect

end
end
s=Test.new
puts s.test

My intent is when i call class Npc(str) i want to get back
3 random values power,speed,smarts 2 derived values => accuracy and =>
health and a derived string => job

like this
joe=Npc.new(‘Joe’)
puts joe

output like “Joe” 11 12 15 10.15 8.8 “smithy”

Le 03 mars à 17:53, August0866 a écrit :

My intent is when i call class Npc(str) i want to get back
3 random values power,speed,smarts 2 derived values => accuracy and =>
health and a derived string => job

like this
joe=Npc.new(‘Joe’)
puts joe

output like “Joe” 11 12 15 10.15 8.8 “smithy”

Ok. Here’s an adapted version of your code.

  • I gave you the way to create a “Roll” objetct, but you don’t really
    need it ; what you need is an “helper function” that returns a
    number. So, I made a module to contain it.
  • Constructors in Ruby always return the new object, regardless of the
    explicit return clause.
  • Rolling dice every time you access one of your base characteristics is
    not the way to go ; here, I assign them at constructing time, and
    give them an accessor.
  • Using the #{} interpolation mechanism is way more legible than the
    concatenation with xxx.to_s (BTW, inspect returns a string, no need
    for to_s).
  • Puts returns nil, not the string passed ; puts test where your test
    method itself is writing on the screen is useless.
  • If you want to convert your object to a string (for instance for
    puts), all you need to do is define a to_s method.

Oh, and last : you should really pay attention to the formatting of
your code…

class Roll
def Roll.roll(base, range)
base+rand(range)
end
end

class Stats
attr_reader :power, :speed, :smarts
def initialize()
@power = Roll.roll(10,8)
@speed = Roll.roll(10,8)
@smarts = Roll.roll(10,8)
end
end

class Attribs < Stats
def acc
(@power.to_f + (@speed.to_f / @smarts.to_f)).round
end

def health
(@power.to_f * (@smarts.to_f / @speed.to_f)).round
end
end

class Npc < Attribs
def initialize(name)
@name = name
super()
end
def job
‘stuff’
end
def to_s
"#{@name} - pw#{power}, sp#{speed}, sm#{smarts}, ac#{acc}, "
“he#{health} - #{job}”
end
end

class Test
def test
a = Roll.roll(10,8)
b = Stats.new
c = Attribs.new
d = Npc.new(‘Joe’)

puts "++++++++++++++++A+++++++++++++++++++"
puts "a object is #{a.to_s}"
puts "a's method is called roll #{a.inspect}"
puts "++++++++++++++++A+++++++++++++++++++"
puts "================B==================="
puts "b is object #{b.inspect}"
puts "b methods are power = #{b.power}, "
puts "speed = #{b.speed}, "
puts "and finaly smarts = #{b.smarts.to_s}"
puts "================B=================="
puts "________________C__________________"
puts "now on to c"
puts "#{c.to_s} is the object name for c"
puts "c has all the methods of a and b plus its own"
puts " c has a power of #{c.power}"
puts " c has a speed of #{c.speed}"
puts " c has a smarts of #{c.smarts}"
puts " plus c has an Accuracy of #{c.acc}"
puts " plus c has a health of #{c.health}"
puts "________________C____________________"
puts "||||||||||||||||D||||||||||||||||||||"

puts 'D is the goal'
puts d.inspect
puts d

end
end
s=Test.new
s.test

Fred

On Mar 3, 1:01 pm, “F. Senault” [email protected] wrote:

Le 03 mars à 17:53, August0866 a écrit :

Thank you for taking time i appreciate the help,

Oh, and last : you should really pay attention to the formatting of
your code…

being painfully new at this i don’t know what you mean, what can i do
better format wise next time

Le 03 mars à 20:39, August0866 a écrit :

On Mar 3, 1:01 pm, “F. Senault” [email protected] wrote:

Oh, and last : you should really pay attention to the formatting of
your code…

being painfully new at this i don’t know what you mean, what can i do
better format wise next time

First and before all, indentation. Try to indent your statements by two
spaces, and to keep the code aligned (the end keyword should be at the
same column than the corresponding def or class keyword, etc).

After that, try to add some space around operators and such.

It makes code much more legible, which is important when you write code
that you’ll have to understand later, and doubly so if you post it to a
public forum where other people have to understand it…

CYA,

Fred

Le 04 mars à 06:52, Arlen C. a écrit :

You could use a regular ol’module for that:

module Roll
def self.roll(base, range)
base + rand(range)
end
end

Indeed. I wanted to make that modification, but it seems I forgot it…
Thanks.

Fred

Hi,

On Tue, Mar 4, 2008 at 5:04 AM, F. Senault [email protected] wrote:

  • I gave you the way to create a “Roll” objetct, but you don’t really
    need it ; what you need is an “helper function” that returns a
    number. So, I made a module to contain it.

class Roll

def Roll.roll(base, range)
base+rand(range)
end
end

You could use a regular ol’module for that:

module Roll
def self.roll(base, range)
base + rand(range)
end
end

Arlen

OK, 2 dumb questions What is the difference between a class and a
module and

What is wrong here, the logic or the syntax

<stub location =( class => Npc, def => job)>

def job
if
@power.to_f > 12 && @smarts.to_f > 14 && @speed.to_f > 14
@job = ‘Adventurer’
elsif
@power.to_f > 14 and (@smarts.to_f <14 or @speed.to_f < 14)
@job = ‘laborer’
elsif
@power.to_f < 14 and (@smarts.to_f > 14 and @speed.to_f > 14)
@job = ‘coureror’
elsif
@power.to_f < 14 and (@smarts.to_f > 14 and @speed.to_f < 14)
@job=‘teacher’
else
@job = ‘commoner’
end
end

the result is sometimes joe is an adventurer otherwise nothing is
output

On Tue, Mar 4, 2008 at 8:50 AM, August0866 [email protected]
wrote:

     @job = 'Adventurer'
    @job = 'commoner'
end

end

the result is sometimes joe is an adventurer otherwise nothing is
output

Not sure, but I think the logic. (@smarts.to_f < 14 or @speed.to_f <
14) will be true the same time (@smarts.to_f > 14 and @speed.to_f <
14) is true. Your decision set is not mutually exclusive because of
that first 12 (What if your 13?). Just a guess…

Todd

On Mar 4, 2008, at 9:50 AM, August0866 wrote:

OK, 2 dumb questions What is the difference between a class and a
module and

A module is a container for methods and a namespace
for constants.

A class is a module (Class inherits from Module) that
provides a framework for instantiating and initializing
instances as well as organizing method lookup through
the inheritance hierarchy.

If you think of classes as blueprints for creating
instances, then modules are like common sub-systems
that are reused in the blueprints of one or more
classes.

Gary W.

On Tue, Mar 4, 2008 at 12:23 PM, Todd B. [email protected]
wrote:

Not sure, but I think the logic. (@smarts.to_f < 14 or @speed.to_f <
14) will be true the same time (@smarts.to_f > 14 and @speed.to_f <
14) is true. Your decision set is not mutually exclusive because of
that first 12 (What if your 13?). Just a guess…

One other thing, though. Maybe you want your code to cascade the
decision like that. In any case, your code works just fine on my
system, as long as I properly #rand my power, smarts, and speed :slight_smile:
On 1.8.6, the if/elsif construct seems to correctly jump out. In
other words, if my power is 13, then it will will leave the loop with
the second result and never get to the fourth.

My distribution for 10_000 people for one run using rand(20 for each
attribute turns out to be…

adventurer: 211
laborer: 2260
courier: 416
teacher: 1261
commoner: 5852

…which doesn’t look too bad.

Todd

On Mar 4, 2:35 pm, Todd B. [email protected] wrote:

On 1.8.6, the if/elsif construct seems to correctly jump out. In
commoner: 5852

…which doesn’t look too bad.

Todd

Thanks Todd,

how would i go about defining a job and its requirements and then
match the npc to the job

job[[Adventurer,14,14,14],[Courier,14,10,14] .... ]

the numbers are the minimum stats for the job these jobs are actually
categories i am going to break them down to specific occupations
next

On Mar 4, 1:23 pm, Todd B. [email protected] wrote:

     @power.to_f > 12 && @smarts.to_f > 14 && @speed.to_f > 14
else
  1. is true. Your decision set is not mutually exclusive because of
    that first 12 (What if your 13?). Just a guess…

Todd

Ok, i’m looking for a true false answer to choose a job. For example
Joe - smarts = 15 power = 9 speed = 17

      adventurer job needs power >= 12 and speed >=14 and smarts

= 14 ?joe an adventurer = false
laborer job needs power >= 12 but smarts and speed <=
12 ?joe laborer = false
mailman job needs power >=12 speed >=12 but smarts
=9 ?joe mailman = false
teacher job needs power <=14 speed <=14 but smarts
=14 ?joe teacher =
</pseudo code>

D’oh i see the problem… i need to rethink the jobs

On Tue, Mar 4, 2008 at 6:04 PM, August0866 [email protected]
wrote:

One other thing, though. Maybe you want your code to cascade the
laborer: 2260
how would i go about defining a job and its requirements and then
match the npc to the job

job[[Adventurer,14,14,14],[Courier,14,10,14] .... ]

the numbers are the minimum stats for the job these jobs are actually
categories i am going to break them down to specific occupations
next

I’ll make a couple assumptions here. I think you want to have minimum
requirements, and then have the job assignment go to the least
requirement. In other words, in your above pseudo, if I had stats
like [14,15,14], I would be an adventurer, and if I had stats
[10,14,14] I would fail to be either (but maybe something else).
Least common denominator?

I would approach it probably in an eccentric way. Most people would
suggest side by side iteration of Arrays (which, IIRC, was added as a
feature to Ruby 1.9). That is probably the best way to do it. I’m
kind of a set theory guy, though, so tend towards slower but
conceptually more sound (to me) methods.

I’d probably build a matrix of the person’s stats and use that…

require ‘matrix’
joblist = [‘adventurer’, ‘courier’]
attributes = [‘power’, ‘smarts’, ‘speed’]
n, j = attributes.length, joblist.length
a = [[14, 14, 14], [14, 10, 14]]
some_poor_guys_stats_copies = Array.new(j, Array.new(n) {rand(20) + 1})
s = Matrix[*some_poor_guys_stats_copies]
e = Matrix[*a]
poss = []
(s - e).to_a.each_with_index do |v, i|
poss << i if (v.select {|j| j >= 0}).length == n
end
p joblist[poss.min] rescue p “Commoner”

It’s not even close to clean, but I like using Matrix for this kind of
thing instead of the side by side iterating.

I suppose it’s way better to use a Hash, too, like so…

requirements = {
:adventurer => {
:power => 14,
:smarts => 14.
:speed => 14
}
:courier => {
:power => 14,
:smarts => 10,
:speed => 14
}

person = {
:power => rand(die_number),
:smarts => rand(die_number),
:speed => rand(die_number)
}

I’m pretty sure you can use the Hash.new block form to build these if
you wanted to.

I should mention also that many people frown upon using the rescue
word for conditional use (in other words, my code above is not good
for application use, but probably ok for a script).

Todd

On Mar 5, 4:36 am, Todd B. [email protected] wrote:

On Wed, Mar 5, 2008 at 3:08 AM, Todd B. [email protected] wrote:

Least common denominator?

I should point out, too, that my use of #min only works if you want to
pick the first on the list that fits. Maybe what you really want are
all the jobs that apply for a person.

Todd

Wow Crunchy code. it will take ma awhile to decipher all the things
going on up there.

originally the idea was first match out. I think that approach may
not be where i end up in the end.

thanks again for the help

On Wed, Mar 5, 2008 at 3:08 AM, Todd B. [email protected] wrote:

Least common denominator?

I should point out, too, that my use of #min only works if you want to
pick the first on the list that fits. Maybe what you really want are
all the jobs that apply for a person.

Todd

On Thu, Mar 6, 2008 at 7:20 PM, August0866 [email protected]
wrote:

Wow Crunchy code. it will take ma awhile to decipher all the things
going on up there.

originally the idea was first match out. I think that approach may
not be where i end up in the end.

thanks again for the help

With comments…

require ‘matrix’
#need this for Matrix class
joblist = [‘adventurer’, ‘courier’]
attributes = [‘power’, ‘smarts’, ‘speed’]
n, j = attributes.length, joblist.length
a = [[14, 14, 14], [14, 10, 14]]
#cheating here, need Hash really
some_poor_guys_stats_copies = Array.new(j, Array.new(n) {rand(20) + 1})
#an array of arrays to be a matrix
s = Matrix[*some_poor_guys_stats_copies]
#make the requirements Matrix
#using e for “employment”, bad choice
e = Matrix[*a]
poss = []
#using poss for possibilities, bad choice
(s - e).to_a.each_with_index do |v, i|
poss << i if (v.select {|j| j >= 0}).length == n
end
p joblist[poss.min] rescue p “Commoner”

Okay, the key here is:

I have two matrices of same dimensions. I take the guys stats,
subtract the requirements, and remove anything that is negative in
each ability check.

I’m not certain at the moment how I would clean that up for production
use, but I have a few ideas. The only dodgy part of the code
(discounting the variable names), IMHO, is the a = [[14, 14, 14], [14,
10, 14]] part. Not because of the logic, but because of the fact
we’re depending on the order of the array called “a”. Like, what if
somebody changes the attributes array? A Hash object would fit better
here even if it requires more code.

hth,
Todd