Learning by doing part 2 - tc game

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

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 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!

Sam

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.

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 …

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

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. :wink:

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:)

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. :wink:

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.

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.

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. :wink:

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 ’
%

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. :wink:

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

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 Wed, Jul 18, 2012 at 2:02 PM, Sebastjan H. [email protected]
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.

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

“Jesús Gabriel y Galán” [email protected] wrote in post
#1069191:

On Wed, Jul 18, 2012 at 2:02 PM, Sebastjan H. [email protected]
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 Thu, Jul 19, 2012 at 10:13 AM, Sebastjan H. [email protected]
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.

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

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.