Learning by doing part 2 - tc game

Jan E. wrote in post #1071102:

Hi,

First of all, don’t misuse instance variables to pass values to methods.
This makes the code obscure and fragile – and it’s simply bad design.
Use method parameters instead (that’s what they’re for). The purpose of
instance variables is to hold the state of an object.

It your case it cannot even work, because you have different objects.
The dragon object cannot access the instance variables from the top
level object.

So the fix for this would be to simply add a parameter to the “effect”
method for the opponent:

def effect(opponent)

And maybe further parameters for other values.

Thank you very much for reminding me of the basic rules.

kind regards
seba

I am back with another question.

I am re-factoring the game and still trying to keep it simple, so bear
with me. I’ve added effects to cards which are activated when played.

In the main file I have this for each turn:

The cards effects are activated (opponent sequence is random)

players = [@hm_player, @ai_player]
@opponent = players.sample

if @opponent == @ai_player
@own = @hm_player
hm_card.effect

@opponent = @hm_player
@own = @ai_player
ai_card.effect

elsif @opponent == @hm_player
@own = @ai_player
ai_card.effect

@opponent == @ai_player
@own = @hm_player
hm_card.effect

else

end


Now, I wanted to keep it simple, so I have the dragon’s effect defined
as follows:

def effect()
@opponent.hp -= card.attack # I’ve just spotted this error, where I
refer to card and in the above code there is still hm_card
end

When I run the game everything goes smoothly until the effect should be
activated. I get this error:

dragon.rb:31:in effect': undefined methodhp’ for nil:NilClass
(NoMethodError)
from game_02.rb:126:in `’

If I puts the opponent.hp it works though.

regards,
seba

I wanted to test this on a simple case, so I wrote the following:

def attack(target)
target -= 200
end

class A
def initialize(name, hp)
@name = name
@hp = hp
end

def hp()
@hp
end

def name()
@name
end

end

players = [] << A.new(“test”, 400)

attack(players[0].hp)

puts players[0].hp

The code runs, but the result is 400. As if the attack method did
nothing.
What am I doing wrong?

On Fri, Aug 3, 2012 at 3:06 PM, Sebastjan H. [email protected]
wrote:

end

players = [] << A.new(“test”, 400)

attack(players[0].hp)

puts players[0].hp

The code runs, but the result is 400. As if the attack method did
nothing.
What am I doing wrong?

The reason is that this line:

target -= 200 is equivalent to this one:
target = target - 200

which is assigning a new value to the local variable of the method.
Once you exit the method, the new value is lost. In order for this to
work, you should pass a reference to a mutable object, so that the
effects of the method are visible outside. For example:

def attack(target)
target.hp -= 200 # supposing target has methods #hp and #hp=
end

later…

attack(player[0])

Hope this helps,

Jesus.

In this line you wrote

target.hp -= 200 # supposing target has methods #hp and #hp=

what is the meaning of the equal sign? #hp=

Because in my case above the target only has hp method.

And if I remodel it to your example:

def attack(target)
target.hp -= 200
end

class A
def initialize(name, hp)
@name = name
@hp = hp
end

def hp()
@hp
end

def name()
@name
end

end

players = [] << A.new(“test”, 400)

attack(players[0])
puts players[0].hp

3.rb:3:in attack': undefined methodhp=’ for #<A:0x8b7d268
@name=“test”, @hp=400> (NoMethodError)
from 3.rb:24:in `’

Once again, I turn to you for your wisdom:)

Why this works:

class Depo
attr_accessor :test

def initialize(test)
@test = test
end

def test()
@test
end
end

bla = Depo.new(“blahdsd”)

def defin (bla)
if defined? test
puts “#{bla.test}”
end
end

defin(bla)

And this doesn’t:

def effect_output(card)
if defined? attack
puts “#{card.name.capitalize} hits with #{card.attack}.”
elsif defined? heal
puts “#{card.name.capitalize} heals for #{card.heal}.”
end
end

The card is a DRAGON class object with method attack defined. It has the
attr_accessor as well as initialize method defined. I don’t even get an
error. Nothing gets putsed.

If I use

if defined? :attack

then I get the message that attack is an undefined method for class
HEAL.

Of course the HEAL class doesn’t have attack method, but it should jump
to elsif, which checks for the heal method. This is true for the HEAL
class.

???

Hi,

The “defined?” operator is simply wrong in this case. It checks the
current scope
for variables, methods etc.

For example:

x = 0
C = 15
puts defined? x # this outputs “local-variable”
puts defined? C # this outputs “constant”
puts defined? a # this outputs nothing (because the result is nil)

You want something completely different, namely check if an object has a
specific method. That’s what Object#respond_to? is for:

def effect_output(card)
if card.respond_to? :attack
puts “#{card.name.capitalize} hits with #{card.attack}.”
elsif card.respond_to? :heal
puts “#{card.name.capitalize} heals for #{card.heal}.”
end
end

This will call “attack” and “heal” only if the card object does actually
have those methods.

What should have made you suspicious is that you never had to specify
any object. How is Ruby then supposed to know that you want to look into
the card object?

By the way, the reason why your program output something after you used
a symbol is that

defined? :attack

returns “expression” (which is truthy).

On Fri, Aug 3, 2012 at 3:44 PM, Sebastjan H. [email protected]
wrote:

In this line you wrote

target.hp -= 200 # supposing target has methods #hp and #hp=

what is the meaning of the equal sign? #hp=

It’s part of the method name. Ruby has a bit of sintactic sugar to
allow calling methods that end with = as if they were assignments:

target.hp = 200

is equivalent to: target.hp=(200).

@name = name

end

players = [] << A.new(“test”, 400)

attack(players[0])
puts players[0].hp

3.rb:3:in attack': undefined method hp=’ for #<A:0x8b7d268
@name=“test”, @hp=400> (NoMethodError)
from 3.rb:24:in `’

If you want the external classes the possibility to read and assign
new values to hp and name, you can use attr_accessor:

class A
attr_accessor :name, :hp
def initialize(name, hp)
@name = name
@hp = hp
end
end

attr_accessor will create methods name, name=, hp and hp= for you.

Jesus.

That sounds great. :slight_smile:

Can we download the game anywhere?

Jan E. wrote in post #1073084:

That sounds great. :slight_smile:

Can we download the game anywhere?

I haven’t uploaded it anywhere, because I am not sure whether I’ll use
github or launchpad yet and even then I have to learn how to use the
repository first as I am (as stated on several occasions:) a complete
noob in the area.

I’ll post here when it’s uploaded.

kind regards,
seba

On Aug 24, 2012, at 3:26 PM, Sebastjan H. wrote:

end
elsif defined? heal
if defined? :attack

then I get the message that attack is an undefined method for class
HEAL.

???

You don’t want defined? here, you want respond_to? as in:

if card.respond_to?(:attack)

do something involving card.attack

end

-Rob

Jan E. wrote in post #1073054:

Hi,

The “defined?” operator is simply wrong in this case. It checks the
current scope
for variables, methods etc.

For example:

x = 0
C = 15
puts defined? x # this outputs “local-variable”
puts defined? C # this outputs “constant”
puts defined? a # this outputs nothing (because the result is nil)

You want something completely different, namely check if an object has a
specific method. That’s what Object#respond_to? is for:

def effect_output(card)
if card.respond_to? :attack
puts “#{card.name.capitalize} hits with #{card.attack}.”
elsif card.respond_to? :heal
puts “#{card.name.capitalize} heals for #{card.heal}.”
end
end

This will call “attack” and “heal” only if the card object does actually
have those methods.

What should have made you suspicious is that you never had to specify
any object. How is Ruby then supposed to know that you want to look into
the card object?

By the way, the reason why your program output something after you used
a symbol is that

defined? :attack

returns “expression” (which is truthy).

Thank you. That helped. I actually was wondering why I didn’t have to
specify an object, but I just couldn’t make the connection.

Anyway, I think I am finished with the game for now. It runs ok and I’ve
also included a couple of different arenas which influence on the
possibility of critical strikes. It’s fun to play regarding it’s my
first game and it runs in the command line:)

I also rewrote all the parts where I misused the instance variables.

kind regards,
seba

Jan E. wrote in post #1073084:

That sounds great. :slight_smile:

Can we download the game anywhere?

finally:)

http://bazaar.launchpad.net/~sebastjan-hribar/dragons/dragons_and_dire_wolves/files

I still have a lot of things planned, but it’ll take me some time.

regards,
seba