HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Communication between two classes in ruby

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
communicationtwobetweenclassesruby

Problem

I cannot understand how I can make two classes work together by passing variables between them.

During the last 5 days, I learned the basics of inheritance, singleton methods and eval methods. I even read about template methods, but since I have no programming background, I'm in no position to learn from other languages.

The only aim here is to learn how to make use of classes. The whole game is an exercise.

The game has 5 classes: Game, Creature, Weapon, Armor, Scene.

Creature class is used to produce the hero and several monsters (currently only a dragon is created).

Weapon and Armor classes (with only name and power attributes) are there to produce equipment and while they may be joined as a single Equipment class with name, defense and attack attributes, currently this is not my concern.

Scene class is there to produce several places/rooms, which is the main point of the exercise. The exercise clearly states "Use one class per room and give the classes names that fit their purpose." Therefore I made a class containing name, history and armor/weapon and monster attributes. The reason for name and history are clearly to be able to give an introduction when the player enters the scene.

The armor/weapon and monster attributes are required to make the player to encounter different armors, weapons to equip and different monsters to fight in different scenes. I tried to pass them as variables (arrays) to the scene class during initialize but couldn't succeed. The roots of this failure probably is the reason why I can't make the two classes work together.

I have to admit that I had a hard time to understand how to pass variables to/between methods in ruby, and this curse is following me in the classes, too.

So, as a last thing, you can see the horrible code in the choices method in Scene class. I cannot exactly explain what I was thinking while coding this. But the only good thing I have done is adding self to the @city = Scene.new

Solution

The problem is more about the organization of the game. Normally you would not want to have two classes tightly coupled to each other.

Also location of some methods are not correct. For example battle method in Creature should not be there. Or you should not need two parameters for that method.

You are suffering from the responsibilities of objects at the moment. I would suggest to rethink this.

For example you can decide that "Battle" is not something creature is responsible but game.

I came up with this code which also requires much refactoring. I tried not to change your code too much.

#game.rb
class World
  attr_accessor :armor_list, :weapon_list, :monsters, :scenes
  def initialize()
    @weapon_list = {
            short:      {name: "Short sword", power: 5 },
        long:       {name: "Long sword", power: 8 },
        two_handed: {name: "Two-Handed sword", power: 12 },
        warhammer:  {name: "Warhammer sword", power: 10 }       
          }

    @armor_list = {
            leather:    {name: "Leather Armor", power: 5 },
        chain:      {name: "Chain Mail", power: 10 },
        plate:      {name: "Plate Mail", power: 15 },
        elven:      {name: "Elven Chain Mail", power: 50 }      
      }

    @monsters = { 
        rat:        {name: "Rat", level: 1 },
        thief:      {name: "Thief", level: 50 },
        dragon:     {name: "Dragon", level: 12 }
     }

    @scenes = [
    { name: "City", history: "The city.", weapons: [@weapon_list[:long]], armors: [@armor_list[:chain]], monsters: [@monsters[:thief], @monsters[:rat]] },
    { name: "Mountain", history:  "The mountain.", weapons: [@weapon_list[:warhammer]], armors: [@armor_list[:chain]], monsters: [@monsters[:dragon]] }
      ]
  end
end 

class Game
  def initialize
    @player = Creature.new name: "You", level: 5
    @world = World.new
  end 

  def play
    @current_scene = Scene.new(@world.scenes.first)
    @current_scene.intro

    while @player.is_alive?
        self.show_choices
    end
  end

  def show_choices
    puts  nil, :weapon => nil}
    @armor = 0
    @weapon = 0
    #3.times rand(7) or 3*rand(7) doesn't create the effect, I tried rand(16)+3 but didn't like it.
    @strength = rand(7) + rand(7) + rand(7)
    @condition = rand(7) + rand(7) + rand(7)
    @life = @level * (rand(8) + 1)
    @power = @strength * (rand(4) + 1)
    @regen = @condition
  end

  def introduce_self()
      puts "You wear "  + (@equipment[:armor]  || "no armor!")
      puts "You carry " + (@equipment[:weapon] || "no no weapon!")
  end 

  def wear_armor(armor)   
    @armor = armor.power
    @equipment[:armor] = armor.name
  end

  def wear_weapon(weapon)
    @weapon = weapon.power
    @equipment[:weapon] = weapon.name
  end

  def attack(defender) 
    [@power + @weapon - defender.armor, 0].max
  end

  def defend(attack)
    @life -= attack
  end

  def regen
    @life += @regen
    @regen
  end

  def is_alive?
    @life > 0
  end 
end

class Weapon
  attr_reader :name, :power
  def initialize(setup)
    @name = setup[:name]
    @power = setup[:power]  end
end

class Armor
  attr_reader :name, :power
  def initialize(setup)
    @name = setup[:name]
    @power = setup[:power]
  end
end

game = Game.new()
game.play()

Code Snippets

#game.rb
class World
  attr_accessor :armor_list, :weapon_list, :monsters, :scenes
  def initialize()
    @weapon_list = {
            short:      {name: "Short sword", power: 5 },
        long:       {name: "Long sword", power: 8 },
        two_handed: {name: "Two-Handed sword", power: 12 },
        warhammer:  {name: "Warhammer sword", power: 10 }       
          }

    @armor_list = {
            leather:    {name: "Leather Armor", power: 5 },
        chain:      {name: "Chain Mail", power: 10 },
        plate:      {name: "Plate Mail", power: 15 },
        elven:      {name: "Elven Chain Mail", power: 50 }      
      }

    @monsters = { 
        rat:        {name: "Rat", level: 1 },
        thief:      {name: "Thief", level: 50 },
        dragon:     {name: "Dragon", level: 12 }
     }

    @scenes = [
    { name: "City", history: "The city.", weapons: [@weapon_list[:long]], armors: [@armor_list[:chain]], monsters: [@monsters[:thief], @monsters[:rat]] },
    { name: "Mountain", history:  "The mountain.", weapons: [@weapon_list[:warhammer]], armors: [@armor_list[:chain]], monsters: [@monsters[:dragon]] }
      ]
  end
end 

class Game
  def initialize
    @player = Creature.new name: "You", level: 5
    @world = World.new
  end 

  def play
    @current_scene = Scene.new(@world.scenes.first)
    @current_scene.intro

    while @player.is_alive?
        self.show_choices
    end
  end

  def show_choices
    puts <<-CHOICES
    What would you like to do here?
    1. Look for armor
    2. Look for weapons
    3. Look for monsters to fight
    4. Go to another place!
    CHOICES
    choice = gets.chomp

    case choice
    when "1"
        self.look_for_armor
    when "2"
        self.look_for_weapons
    when "3"
        self.look_for_monsters
    when "4"
          puts "bad choice, since I am not ready yet!"  
    else
          puts "Can't you read?"
          exit
    end
  end

  def look_for_monsters
      monster = @current_scene.monsters.first
      battle [@player, monster]
  end   

  def look_for_armor
      armor = select_equipment @current_scene.armors
      return if armor.nil?
      @player.wear_armor(@current_scene.armor_picked(armor))   
  end 

  def look_for_weapons
      weapon = select_equipment @current_scene.weapons
      return if weapon.nil?
      @player.wear_weapon(@current_scene.weapon_picked(weapon))
  end

  def select_equipment(equipment)
    @player.introduce_self

    puts "You wanna some equipment?"
    equipment.each_with_index do |item, i|
      puts "#{i+1}. #{item.name}"
    end

    gets.chomp.to_i - 1
  end 

  def battle(opponents) 
    attack_turn = 0
    while opponents.all?(&:is_alive?)
    attacker = opponents[attack_turn]
    defender = opponents[(attack_turn + 1) % 2]

    attack = attacker.attack(defender)
    defender.defend(attack)
    puts "#{attacker.name} hit #{defender.name} with #{attack} points of damage!"
    puts "#{defender.name} regenerates #{defender.regen} points of life!" if defen

Context

StackExchange Code Review Q#10312, answer score: 3

Revisions (0)

No revisions yet.