Hi, I am trying to learn a bit more and I've decided to make my own little trading card sort of a game. On the link below there are two files I've written so far. https://docs.google.com/folder/d/0B7TkpCLQg89MemVG... The problem is, that currently the summoned beast is predefined. And I would like to make it so that user picks which one they will summon for battle. If I go via gets.chomp, then the result is a string and the code doesn't accept that. thx regards, seba
on 2012-07-10 11:48
on 2012-07-10 12:21
Hi, You don't create the beast anywhere. Instead, you just get the name from the user and then call "armor" on this string -- which of course doesn't work. I guess you meant to write something like this: beast_name = gets.chomp beast = Blue_Scale_DRAGON.new beast_name and then ...beast.armor
on 2012-07-10 12:39
Yes, that would be ok, but that means that the player has to create all
beasts themselves. I would like to have the beasts and later also some
spells generated randomly and have them available for player to choose
which ones they will use.
Like so:
1. generate 10 beasts
2. generate 10 spells
have these available in either arrays of hashes and then decide each
turn which one to use.
Something like this.
Just now I've tried this:
from line 33 onwards:
---------------------------
prompt
beast = gets.chomp
if beast == "1"
select = derimor
elsif beast == "2"
select = barador
end
while player_hp > 0 and ai_hp > 0
.
.
.
.
------------------------
However, this gets messy if there are a lot of beasts and spells.
regards,
seba
Jan E. wrote in post #1068101:
> Hi,
>
> You don't create the beast anywhere. Instead, you just get the name from
> the user and then call "armor" on this string -- which of course doesn't
> work.
>
> I guess you meant to write something like this:
>
> beast_name = gets.chomp
> beast = Blue_Scale_DRAGON.new beast_name
>
> and then ...beast.armor
on 2012-07-10 12:48
When you've already got an array with the beast names, you can use the player input as an index: # beast_names is the array with the 10 possible names beast_name = beast_names[gets.chomp.to_i - 1] The "- 1" is necessary if your start numbering the names with 1 instead of 0. Of course, you should add some kind of error handling for invalid input.
on 2012-07-10 12:53
On Tue, Jul 10, 2012 at 12:39 PM, Sebastjan H. <lists@ruby-forum.com> wrote: > have these available in either arrays of hashes and then decide each > turn which one to use. You can do that easily: beasts = %w{derimor barador}.map {|name| Blue_Scale_DRAGON.new name} puts "Choose beast to summon:" beasts.each_with_index {|b,i| puts "#{i+1}.- #{b.dragon_name}"} beast = beasts[(gets.to_i) - 1] Jesus.
on 2012-07-10 12:58
"Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post #1068106: > On Tue, Jul 10, 2012 at 12:39 PM, Sebastjan H. <lists@ruby-forum.com> > wrote: >> have these available in either arrays of hashes and then decide each >> turn which one to use. > > You can do that easily: > > beasts = %w{derimor barador}.map {|name| Blue_Scale_DRAGON.new name} > puts "Choose beast to summon:" > beasts.each_with_index {|b,i| puts "#{i+1}.- #{b.dragon_name}"} > beast = beasts[(gets.to_i) - 1] > > > Jesus. thank you both, I'll try these suggestions and post back if I run into problems. Always a pleasure learning from masters:) regards, seba
on 2012-07-10 13:04
"Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post #1068106: > You can do that easily: > > beasts = %w{derimor barador}.map {|name| Blue_Scale_DRAGON.new name} I would create only the chosen beast and not all possible ones. This seems more elegant to me.
on 2012-07-10 13:33
Jan E. wrote in post #1068109: > "Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post > #1068106: >> You can do that easily: >> >> beasts = %w{derimor barador}.map {|name| Blue_Scale_DRAGON.new name} > > I would create only the chosen beast and not all possible ones. This > seems more elegant to me. Maybe I need to clarify a bit more. I would have the game generate a defined number of beasts and spells. The player would then simply choose the ones for battle each turn. The player would not generate the beasts nor spells. They would only use them. This is the idea for now. I may change or expand later. I think I will expand the game in the direction that the player will choose how many beasts and spells of each kind they want generated. For example, I want 5 blue scale dragons, five great dire wolfs and five earth spells. This would then be generated for the player to use. This is just the basis for the game. The basic mechanisms. regards,
on 2012-07-10 13:58
Sebastjan H. wrote in post #1068110: > I would have the game generate a defined number of beasts and spells. > The player would then simply choose the ones for battle each turn. The > player would not generate the beasts nor spells. They would only use > them. This is the idea for now. I may change or expand later. The player doesn't generate anything, it's all done by the program. My suggestion has nothing to do with the game logic, it's only a technical matter: Rather than actually creating all possible beasts (which will lead to unnecessary objects), I'd only generate the *names* and later create the object for the chosen name. Let's say the player has to choose one beast from 1000 possible beasts. Why would you want to create all 1000 possible beast objects and then throw away the 999 which the player didn't select? Wouldn't it make more sense to only have 1000 strings for the names and wait with the object creation until the player has actually chosen one name? Maybe you're thinking too much in terms of physical objects. In order to "generate a beast", you don't actually have to create a beast object. It's enough to have the information which identifies this beast (in this case the name).
on 2012-07-10 14:16
Jan E. wrote in post #1068114: > Sebastjan H. wrote in post #1068110: >> I would have the game generate a defined number of beasts and spells. >> The player would then simply choose the ones for battle each turn. The >> player would not generate the beasts nor spells. They would only use >> them. This is the idea for now. I may change or expand later. > > The player doesn't generate anything, it's all done by the program. > > My suggestion has nothing to do with the game logic, it's only a > technical matter: Rather than actually creating all possible beasts > (which will lead to unnecessary objects), I'd only generate the *names* > and later create the object for the chosen name. > > Let's say the player has to choose one beast from 1000 possible beasts. > Why would you want to create all 1000 possible beast objects and then > throw away the 999 which the player didn't select? Wouldn't it make more > sense to only have 1000 strings for the names and wait with the object > creation until the player has actually chosen one name? > > Maybe you're thinking too much in terms of physical objects. In order to > "generate a beast", you don't actually have to create a beast object. > It's enough to have the information which identifies this beast (in this > case the name). You're right, of course:) I misunderstood you earlier. I still need to adapt the thinking process a bit and this kind of knowledge helps a lot. On the other hand, I am still considering this to be a small scale game with predefined let's say only 15 to 20 cards. However, If I were to expand it I need this adaptation. thx again. regards, seba
on 2012-07-10 14:31
Sebastjan H. wrote in post #1068117: > On the other hand, I am still considering this to be a small scale game > with predefined let's say only 15 to 20 cards. However, If I were to > expand it I need this adaptation. Well, even for 1000 objects you probably won't notice an actual difference in speed. But personally I find it more "clean" to only create objects you actually need.
on 2012-07-10 15:18
Jan E. wrote in post #1068119: > Sebastjan H. wrote in post #1068117: >> On the other hand, I am still considering this to be a small scale game >> with predefined let's say only 15 to 20 cards. However, If I were to >> expand it I need this adaptation. > > Well, even for 1000 objects you probably won't notice an actual > difference in speed. But personally I find it more "clean" to only > create objects you actually need. One more thing, are we talking about the same "selection"? I was referring to selection of one of the beasts from the deck for each turn of the battle. And I think you're referring to the deck selection. Obviously the game needs both, they're just different steps. And for the deck selection I only wanted the player input on the card types (beasts, spells,...). One may want to play only with let's say beasts while others prefer spells. And that's the deck selection. Once the deck is determined, players select cards for each turn. regards, seba
on 2012-07-10 16:40
On Tue, Jul 10, 2012 at 3:18 PM, Sebastjan H. <lists@ruby-forum.com> wrote: > One more thing, are we talking about the same "selection"? I was > referring to selection of one of the beasts from the deck for each turn > of the battle. > > And I think you're referring to the deck selection. > > Obviously the game needs both, they're just different steps. > And for the deck selection I only wanted the player input on the card > types (beasts, spells,...). One may want to play only with let's say > beasts while others prefer spells. And that's the deck selection. Once > the deck is determined, players select cards for each turn. What Jan is saying is that you should defer the creation of the actual Ruby object until the last time, to avoid creating objects that are not going to be used. In my opinion, the last responsible time is the one where you need some value from the object. So if you need to actually create the objects or not for the deck selection depends on if the "key" (the name) is enough for the user to choose or not. For example: cards = %w{derimor barador fireball invisibility} # all the "keys" of the possible cards deck_card_names = [] while deck_card_names.size < 15 # or 20 puts "Choose cards for deck" cards.each_with_index {|c,i| "#{i+1}.- #{c}"} card = cards[gets.to_i - 1] print "quantity: " qty = gets.to_i qty.times {deck_card_names << card} end while true puts "Choose card to play" deck_cards_name.each_with_index {|c,i| "#{i+1}.- #{c}"} card = Blue_Scale_DRAGON.new deck_cards_name[gets.to_i - 1] end Of course if you need the info of the card at choosing time, you might need to instantiate the cards when choosing, for example if you need to know the type or whatever. Depending on how you plan to generate and use the cards, there might be better choices than an array. If you might have a number of identical cards, maybe a hash of "name" => qty or even "name" => {"qty": 4, "type":spell} or more complex stuff might be more appropriate. Jesus.
on 2012-07-10 22:21
Thank you for the clarification. I understand the difference and I have
to first fully determine the logic and the mechanics I want and then
adjust the code.
One side note, why this: puts "#{beast} strikes with: " produces this:
#<Blue_Scale_DRAGON:0x8a225d0> strikes with:?
I am guessing it has to with the class, but would I get the actual name
of the beast which dealt the damage?
regards
seba
on 2012-07-10 22:42
> From: Sebastjan H. <lists@ruby-forum.com> > > One side note, why this: puts "#{beast} strikes with: " produces > this: > #<Blue_Scale_DRAGON:0x8a225d0> strikes with:? When I've seen similar things in my code, I resort to using "#{beast}.to_s to coearse the variable into a string. I'm sure one of the more knowledgeable folks will explain the why (because I have always thought that's what #{variable} did without using the .to_s). Wayne
on 2012-07-10 22:55
Wayne Brisette wrote in post #1068186: >> From: Sebastjan H. <lists@ruby-forum.com> >> >> One side note, why this: puts "#{beast} strikes with: " produces >> this: >> #<Blue_Scale_DRAGON:0x8a225d0> strikes with:? > > When I've seen similar things in my code, I resort to using > "#{beast}.to_s to > coearse the variable into a string. I'm sure one of the more > knowledgeable folks > will explain the why (because I have always thought that's what > #{variable} did > without using the .to_s). > > > Wayne Hi, I've tried it and I got #<Blue_Scale_DRAGON:0x90653ac>.to_s I've tried other variations with .to_s and still doesn't work. regards seba
on 2012-07-10 23:05
Sebastjan H. wrote in post #1068181: > One side note, why this: puts "#{beast} strikes with: " produces this: > #<Blue_Scale_DRAGON:0x8a225d0> strikes with:? > > I am guessing it has to with the class, but would I get the actual name > of the beast which dealt the damage? How is the object supposed to know that you want the value of @name when you transform it into a string? You have to define a custom "to_s" method. If you don't, the object will use the one provided by the Object class (which returns this identifier string). But in this case it makes more sense to simply get the name: "#{beast.dragon_name} strikes with: "
on 2012-07-10 23:09
Jan E. wrote in post #1068190: > Sebastjan H. wrote in post #1068181: >> One side note, why this: puts "#{beast} strikes with: " produces this: >> #<Blue_Scale_DRAGON:0x8a225d0> strikes with:? >> >> I am guessing it has to with the class, but would I get the actual name >> of the beast which dealt the damage? > > How is the object supposed to know that you want the value of @name when > you transform it into a string? You have to define a custom "to_s" > method. If you don't, the object will use the one provided by the Object > class (which returns this identifier string). > > But in this case it makes more sense to simply get the name: > > "#{beast.dragon_name} strikes with: " Jan thank you again. I should have seen this. I think I best turn in:) regards, seba
on 2012-07-10 23:13
On 07/11/2012 08:21 AM, Sebastjan H. wrote: > regards > seba > You could define a #to_s method on your dragon class, in which case you would probably see what you want in that string interpolation. It might return the @name as a string, or some human readable representation of the class like "Blue Scale Dragon" depending on what you want the output to say. A better approach might be to use your existing #dragon_name method in the interpolation like so; "#{beast.dragon_name}" You might decide this is a bit clunky and change that method to simply #name which makes it a bit more convenient when displaying messages about various different beast instances without having to know which kind of beast they are. If you were to do this, your definition could become; attr_reader :name to be called like; "#{beast.name}" You could take it a step further and have a base Beast class which defines all the common methods and attributes of a beast, and then make the Blue Scale Dragon a subclass. BlueScaleDragon < Beast So many possibilities, hope that helps! http://ruby-doc.org/core-1.9.3/Module.html#method-... Sam
on 2012-07-13 15:26
I've done quite some reprogramming according to the advises above and I
need help yet again.
1. I haven't uploaded the new files yet as google docs isn't really the
best solution for this. Which brings me to a sub-question: What
repository is best (I mean most user friendly for a beginner). I am
using Bazaar offline and I've had some difficulties using the Launchpad.
2. Launchpad requires me to state the license. If I have a project like
this (consisting of several files), where do I state the license and is
this entirely up to me as the author?
3. And for the big one:
I am using a hash with beast names and types:
--------------------------------------------------------------------
possible_cards = {"derimor" => "dragon", "barador" => "dragon",
"teragon" => "dragon", "gali" => "dragon", etc.....
--------------------------------------------------------------------
I also get the user input for the number of each type:
--------------------------------------------------------------------
## Select the number of dragons
def select_number_of_dragons()
puts "Select the number of dragons (max. 5)"
@dragons_qty = gets.chomp.to_i
if @dragons_qty < 1 or @dragons_qty > 5
puts "The number of dragons is invalid. Enter a number between 1 and
5."
return select_number_of_dragons()
end
return @dragons_qty
end
--------------------------------------------------------------------
Now I would like to have as many beasts/spells from the hash generated
as user specified. If dragon_qty is 4 then the first (or maybe also
random) 4 dragons from the hash are to be generated.
I've tried the iterating with loop "for i in 0..dragon_qty" but that
actually multiplies each dragon by dragon_qty:(
thank you.
kind regards,
seba
on 2012-07-13 21:10
Sebastjan H. wrote in post #1068570: > I've done quite some reprogramming according to the advises above and I > need help yet again. > > 1. I haven't uploaded the new files yet as google docs isn't really the > best solution for this. Which brings me to a sub-question: What > repository is best (I mean most user friendly for a beginner). I am > using Bazaar offline and I've had some difficulties using the Launchpad. > > 2. Launchpad requires me to state the license. If I have a project like > this (consisting of several files), where do I state the license and is > this entirely up to me as the author? > > 3. And for the big one: > > I am using a hash with beast names and types: > -------------------------------------------------------------------- > possible_cards = {"derimor" => "dragon", "barador" => "dragon", > "teragon" => "dragon", "gali" => "dragon", etc..... > > -------------------------------------------------------------------- > > I also get the user input for the number of each type: > > -------------------------------------------------------------------- > ## Select the number of dragons > def select_number_of_dragons() > puts "Select the number of dragons (max. 5)" > @dragons_qty = gets.chomp.to_i > if @dragons_qty < 1 or @dragons_qty > 5 > puts "The number of dragons is invalid. Enter a number between 1 and > 5." > return select_number_of_dragons() > end > return @dragons_qty > end > -------------------------------------------------------------------- > > Now I would like to have as many beasts/spells from the hash generated > as user specified. If dragon_qty is 4 then the first (or maybe also > random) 4 dragons from the hash are to be generated. > > I've tried the iterating with loop "for i in 0..dragon_qty" but that > actually multiplies each dragon by dragon_qty:( > > thank you. > > kind regards, > seba I've also tried something with until: ------------------------------------------------------------------- begin possible_cards.each do |name, type| if type == "dragon" deck_dragons << name end end end until deck_dragons.length.to_i == @dragons_qty.to_i ------------------------------------------------------------------- ... but it doesn't work either
on 2012-07-14 10:53
>> >> Now I would like to have as many beasts/spells from the hash generated >> as user specified. If dragon_qty is 4 then the first (or maybe also >> random) 4 dragons from the hash are to be generated. >> I think random would be better if I add more beasts and spells in the overall possible deck. I still trying to do it though... regards, seba
on 2012-07-14 11:07
Sebastjan H. wrote in post #1068699: > I think random would be better if I add more beasts and spells in the > overall possible deck. First of all, you should change the structure of possible_cards to this: possible_cards = { :dragons => ["derimor", "barador", ...], :another_type => ["foo", "bar", ...], ... } So the types are the keys, and the corresponding names are the values. Then you can simply use Array#sample to select e. g. 4 dragons: deck_dragons = possible_cards[:dragons].sample 4 Your mistake was that you iterated over possible_cards for every selection. This would always give you the first dragon. And in the second example you forgot the "break", which would put *all* dragons into the deck for every selection. I've some other notes regarding the style, but I'll write that down in a second post ...
on 2012-07-14 12:07
As to the style:
You seem to be coming from a language like Java or so. Note that Ruby
does *not* have a "do-until" or a "do-while" loop. You have to use an
endless loop with a conditional "break" at the end.
This "begin-end-until" actually looks like a "do-until" loop, but it's
not. It's just the same as "until ... do ... end". I'd actually avoid
this notion completely, because it's very ambiguous and hard to read.
Use the appended "until", "while", "if" etc. only for single line
statements.
Ruby obviously does have a "for-in" loop, but I'd avoid that, too. It's
cumbersome, it doesn't introduce a new scope (loop variables are visible
from the outside), and it's not very "Rubyish". Use "each" instead.
You generally use a very low level programming style with lots of loops
and manually filling arrays. Of course, you can do that in Ruby. But
Ruby also allows you to use a more highlevel approach with methods like
"select", "reject", "map" etc.
For example, if you want to select all even numbers between 1 and 100,
you can do that by writing
even_numbers = []
for i in 1..100
if i % 2 == 0
even_numbers << i
end
end
This translates directly to Java code. But in Ruby you'd rather use the
"select" method and let the computer do all the lowlevel stuff
(initializing an array, looping over the numbers and checking for the
condition):
even_numbers = (1..100).select {|i| i.even?}
or shorter
even_numbers = (1..100).select &:even?
As you can see, this is much more readable. Of course, you won't always
find a suitable method for your specific problem.
But I think you should generally get away from this lowlevel procedural
style and think in more highlevel terms like "selecting", "combining"
etc.
This should lead to more compact and better readable code.
on 2012-07-14 12:52
Jan E. wrote in post #1068742: > As to the style: > > You seem to be coming from a language like Java or so. Actually, I have no programming background. I started with VBA for Applications to streamline tasks at work and then I wanted something more and to be honest I was goggling for the programming language with the best ratio of user-friendliness/power. I ended up with Ruby and I love it. > But I think you should generally get away from this lowlevel procedural > style and think in more highlevel terms like "selecting", "combining" > etc. > > This should lead to more compact and better readable code. This is definitely my goal. I'm reading books on ruby and I will also take some online tutorials to achieve this. However, I couldn't do it without the great help from the community. So Jan and others, thank you very much. And of course, to learn from practice, I am just undertaking projects like this as I go along. Could I bother you to have a look at the other two questions I wrote about repository in licences? thx, seba
on 2012-07-15 08:59
> > Could I bother you to have a look at the other two questions I wrote > about repository in licences? > I wanted to say repository AND licences of course:)
on 2012-07-15 16:18
Well, I've never used Bazaar. I think most people use Git together with GitHub. As to the licence: As far as I understand, Ruby itself doesn't force you to use a specific licence. It may be different if you include external libraries. But I'm really no lawyer. ;-)
on 2012-07-15 20:43
Jan E. wrote in post #1068806: > Well, I've never used Bazaar. I think most people use Git together with > GitHub. > > As to the licence: As far as I understand, Ruby itself doesn't force you > to use a specific licence. It may be different if you include external > libraries. But I'm really no lawyer. ;-) hi, thx for the info. now, before the battle, as suggested above, I have list of cards with names and attributes. code for this: @deck.each_with_index {|b,i| puts "#{i+1}. #{b.name}\n#{b.type}\n#{b.attributes}"} My question is about the output: Type: dragon. Attack: 114. Armor: 50. 1. gali The order is reversed.
on 2012-07-15 21:41
Yes. The problem is that b.attributes *outputs* a string instead of returning it (at least in the code you posted first). So the attributes are displayed before the actual "puts" is executed, while the "b.attributes" expression simply returns nil. You'll simply have to change the "attributes" method. Outputting should generally be restricted to either the top level or prominent methods. All other methods should *return* their value.
on 2012-07-16 23:39
On Jul 14, 2012, at 03:07 , Jan E. wrote: > You seem to be coming from a language like Java or so. Note that Ruby > does *not* have a "do-until" or a "do-while" loop. You have to use an > endless loop with a conditional "break" at the end. > > This "begin-end-until" actually looks like a "do-until" loop, but it's > not. It's just the same as "until ... do ... end". I'd actually avoid > this notion completely, because it's very ambiguous and hard to read. > Use the appended "until", "while", "if" etc. only for single line > statements. No, begin/end/(while|until) is NOT the same as (while|until)/end: % ruby -e 'begin puts "bad"; end while false' bad % ruby -e 'while false do puts "bad"; end ' %
on 2012-07-17 01:01
Yeah, I actually remembered there was this strange "do-while" loop, which we're not supposed to use: http://bit.ly/Mdn8W0 So here it is. And you can forget it right away. ;-)
on 2012-07-17 09:58
Jan E. wrote in post #1068984: > Yeah, I actually remembered there was this strange "do-while" loop, > which we're not supposed to use: > > http://bit.ly/Mdn8W0 > > So here it is. And you can forget it right away. ;-) well, I like to stay away from until/while, however, I am not sure how to avoid it in the case of running combat turns: while player_hp > 0 and ai_hp > 0 . .# players play cards . . end regards, seba
on 2012-07-17 11:05
Sebastjan H. wrote in post #1069017: > well, I like to stay away from until/while, however, I am not sure how > to avoid it in the case of running combat turns: No, "until" and "while" are perfectly fine, I was talking about this particular structure: begin #body end while #condition This one you should avoid, because it behaves unexpectedly (and is just ugly). In contrast to all other uses of the "while" modifier, it executes the body *before* checking the condition -- like a "do-while" loop in other languages. If you actually need a "do-while" loop, use an endless loop with a conditional "break" at the end of the loop body: loop do #body break unless #condition end The same goes for "begin-end-until", of course. In this case you need a "break if" at the end.
on 2012-07-18 14:02
I need (hopefully) one final help for this game:)
I am trying to program the battle turn and I just wanted to know whether
there is any better or more elegant solution other than branching so
much:
-------------------------------------------------------------
## Here it needs to branch according to card attributes: spells don't
have attacks.
if card.type == "armor_spell"
player_hp = player_hp + card.armor
elsif card.type == "healing_spell"
player_hp = player_hp + card.heal
elsif card.type == "debuff_spell"
if ai_card.type == "dragon" or ai_card.type == "dire wolf"
ai_card.attack = ai_card.attack - card.debuff
end
elsif card.type == "dragon" or card.type == "dire wolf"
if ai_card.type == "dragon" or ai_card.type == "dire wolf"
player_hp = player_hp + card.armor - ai_card.attack
puts "#{ai_card.name} strikes with " + ai_card.attack.to_s
puts "\n"
end
if ai_card.type == "armor_spell"
ai_hp = ai_hp + ai_card.armor
elsif ai_card.type == "healing_spell"
ai_hp = ai_hp + ai_card.heal
elsif ai_card.type == "debuff_spell"
if card.type == "dragon" or card.type == "dire wolf"
card.attack = card.attack - ai_card.debuff
end
elsif ai_card.type == "dragon" or ai_card.type == "dire wolf"
ai_hp = ai_hp + ai_card.armor - card.attack
puts "#{card.name} strikes with: " + card.attack.to_s
end
@deck.delete(card)
@ai_deck.delete(ai_card)
puts "Player health is #{player_hp}."
puts "Ai health is #{ai_hp}."
-------------------------------------------------------------
thank you very much.
Best regards,
seba
on 2012-07-18 14:21
On Wed, Jul 18, 2012 at 2:02 PM, Sebastjan H. <lists@ruby-forum.com> wrote: > player_hp = player_hp + card.armor > if ai_card.type == "dragon" or ai_card.type == "dire wolf" > ai_hp = ai_hp + ai_card.heal > > > @deck.delete(card) > @ai_deck.delete(ai_card) > puts "Player health is #{player_hp}." > puts "Ai health is #{ai_hp}." > ------------------------------------------------------------- > thank you very much. One approach that implies a huge refactor of your datastructures, would be to have each card object implement its own logic on the game state: class ArmorCard attr_reader :armor_level def initialize armor @armor_level = armor end def perform_action game_state game_state.increment_player_hp(armor) end end class HealingSpellCard attr_reader :spell_level def initialize spell_level @spell_level = spell_level end def perform_action game_state game_state.increment_player_hp(spell_level * 2) #healing spells heal double their level (example of spell logic) end end Then you only need to call the perform_action method in each card object. If you have common logic, such as attack card only differing in name and attack value, but the attack logic is the same, then you could model it with a class hierarchy: class Card def is_attack? false end end class AttackCard < Card def is_attack? true end def perform_action game_state game_state.increment_player_hp(armor) if game_state.ai_card.is_attack? game_state.increment_player_hp(-ai_card.attack) end end end class Dragon < AttackCard attr_reader :armor, :attack def initialize armor, attack @armor = armor @attack = attack end end And so on. You might have different classes depending on characteristics or other ways to model it: maybe AttackCard, SpellCard, etc could be modules you mixin in specific cards, for example if some card can be both. Then maybe you could have a general implementation of perform_action in the Card class. It depends. I hope this gives you some ideas. Jesus.
on 2012-07-19 10:13
"Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post #1069191: > On Wed, Jul 18, 2012 at 2:02 PM, Sebastjan H. <lists@ruby-forum.com> > wrote: >> player_hp = player_hp + card.armor >> if ai_card.type == "dragon" or ai_card.type == "dire wolf" >> ai_hp = ai_hp + ai_card.heal >> >> >> @deck.delete(card) >> @ai_deck.delete(ai_card) >> puts "Player health is #{player_hp}." >> puts "Ai health is #{ai_hp}." >> ------------------------------------------------------------- >> thank you very much. > > > One approach that implies a huge refactor of your datastructures, > would be to have each card object implement its own logic on the game > state: > > class ArmorCard > attr_reader :armor_level > def initialize armor > @armor_level = armor > end > > def perform_action game_state > game_state.increment_player_hp(armor) > end > end > > class HealingSpellCard > attr_reader :spell_level > def initialize spell_level > @spell_level = spell_level > end > > def perform_action game_state > game_state.increment_player_hp(spell_level * 2) #healing spells > heal double their level (example of spell logic) > end > end > > Then you only need to call the perform_action method in each card > object. If you have common logic, such as attack card only differing > in name and attack value, but the attack logic is the same, then you > could model it with a class hierarchy: > > class Card > def is_attack? > false > end > end > > class AttackCard < Card > def is_attack? > true > end > > def perform_action game_state > game_state.increment_player_hp(armor) > if game_state.ai_card.is_attack? > game_state.increment_player_hp(-ai_card.attack) > end > end > end > > class Dragon < AttackCard > attr_reader :armor, :attack > def initialize armor, attack > @armor = armor > @attack = attack > end > end > > And so on. You might have different classes depending on > characteristics or other ways to model it: maybe AttackCard, > SpellCard, etc could be modules you mixin in specific cards, for > example if some card can be both. Then maybe you could have a general > implementation of perform_action in the Card class. It depends. > > I hope this gives you some ideas. > > Jesus. Hi Jesus, I think you are right, this is probably the way to go and much better than branching or making different methods for different combinations of cards (would make game expansions impossible). However, I don't understand the "increment_player_hp". This is just your example, right? I'd have to define this I guess. Furthermore, according to your example above for the AttackCard there is only the definition for the player attack. I'd have to make two of those, also for the AI, right?. And for all other card types as well. regards, seba
on 2012-07-19 10:38
On Thu, Jul 19, 2012 at 10:13 AM, Sebastjan H. <lists@ruby-forum.com> wrote: >>> @ai_deck.delete(ai_card) >> class ArmorCard >> class HealingSpellCard >> >> >> end >> And so on. You might have different classes depending on > > I think you are right, this is probably the way to go and much better > than branching or making different methods for different combinations of > cards (would make game expansions impossible). > > However, I don't understand the "increment_player_hp". This is just your > example, right? I'd have to define this I guess. Yes, this is an example. You might have a GameState class with these kind of utility methods, or any other mechanism for accessing the game state. For example you could also have just an attr_accessor for player_hp and do game_state.player_hp += 5, but in general I like those higher levell methods, because you might use them for integrity checks, or triggering other logic that might otherwise need to repeat everywhere. Another example: every time you modify the player_hp, you might want to check if the player is dead and thus end the game or whatever. If you have the logic for modifying that value in a single place you only that logic in one place (DRY). > Furthermore, according to your example above for the AttackCard there is > only the definition for the player attack. I'd have to make two of > those, also for the AI, right?. And for all other card types as well. Yes, not sure how that works though. You and the AI each play a card, then you activate the player's card that does something to both players, then you activte the AI card which does something to both players? Are the cards the same type for both? Do they do something different depending if it's the player or the AI the one playing the card? If the answers are yes, yes and no, you could do soemthing like: # this would be the game loop making the player and the ai choose cards and activated them in turn: game_state.set_active_player :player game_state.play_card_for_active_player game_state.set_active_player :ai game_state.play_card_for_active_player class AttackCard < Card def is_attack? true end def perform_action game_state game_state.increment_active_player_hp(armor) if game_state.opponent_card.is_attack? game_state.increment_active_player_hp(-game_state.opponent_card.attack) end end end So now, both players (player and ai) are the same from the card's point of view. For a card there's just the "active player" who is the one activating the card and the "opponent", who is... well, the opponent :). If, on the other hand, a card needs to know if it's the player or the ai the one activating it, you can also model that information in the game state and have the card check who is the active player. Another option would be to have two sets of cards that implement the appropriate logic, for example PlayerAttackCard and AIAttackCard and so the decks don't share card implementations. So many possibilities... :D. Jesus.
on 2012-07-19 10:48
Thank you very much, this is very helpful, I like your solution with the active player. I'll go back to the drawing board for quite some time I guess:) since this isn't the only project. regards, seba
on 2012-08-03 00:38
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 method `hp' for nil:NilClass
(NoMethodError)
from game_02.rb:126:in `<main>'
If I puts the opponent.hp it works though.
regards,
seba
on 2012-08-03 02:47
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.
on 2012-08-03 09:14
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
on 2012-08-03 15:06
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 2012-08-03 15:26
On Fri, Aug 3, 2012 at 3:06 PM, Sebastjan H. <lists@ruby-forum.com> 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.
on 2012-08-03 15:44
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 method `hp=' for #<A:0x8b7d268
@name="test", @hp=400> (NoMethodError)
from 3.rb:24:in `<main>'
on 2012-08-03 16:29
On Fri, Aug 3, 2012 at 3:44 PM, Sebastjan H. <lists@ruby-forum.com> 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 `<main>' 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.
on 2012-08-24 21:26
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.
???
on 2012-08-24 23:20
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 2012-08-26 15:12
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
on 2012-08-27 06:45
Jan E. wrote in post #1073084: > That sounds great. :-) > > 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 2012-08-27 13:59
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
on 2012-09-03 21:55
Jan E. wrote in post #1073084: > That sounds great. :-) > > Can we download the game anywhere? finally:) http://bazaar.launchpad.net/~sebastjan-hribar/drag... I still have a lot of things planned, but it'll take me some time. regards, seba
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.