From: "Jesús Gabriel y Galán" Date: 2012-07-19T17:37:39+09:00 Subject: Re: learning by doing part 2 - tc game On Thu, Jul 19, 2012 at 10:13 AM, Sebastjan H. wrote: > "Jesús Gabriel y Galán" wrote in post > #1069191: >> On Wed, Jul 18, 2012 at 2:02 PM, Sebastjan H. >> 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. 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.