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 step of the Game.initialize(), so that I can pass the game object to the city object. After that I got lost and need your help. #game.rbclass Game  attr_accessor :armor_list, :weapon_list, :equipment  def initialize()    #Game creates the city and mountain as two separate scenes    @city = Scene.new(City, The city., @cm, @ls, , self)    @mountain = Scene.new(Mountain, The mountain., @em, @wh, @d, self)    #Game creates armor and weapon array-name-holders, I couldn't find another way to make the equipment-selection work    @a = Armor.new(armor_list, 0)    @w = Weapon.new(weapon_list, 0)    #Game creates weapons    @ss = Weapon.new(Short sword, 5)    @ls = Weapon.new(Long sword, 8)    @ts = Weapon.new(Two-Handed sword, 12)    @wh = Weapon.new(Warhammer, 10)    #Game creates armors    @la = Armor.new(Leather Armor, 5)    @cm = Armor.new(Chain Mail, 10)    @pm = Armor.new(Plate Mail, 15)    @em = Armor.new(Elven Chain Mail, 50)    #Game creates a hero and a dragon    @h = Creature.new(name = You, level = 5)    @d = Creature.new(name = Dragon, level = 12)    #The default equipment list to provide an arsenal. I wish to separate them and put 1 or 2 into each scene    @armor_list = [@a, @la, @cm, @pm, @em]    @weapon_list = [@w, @ss, @ls, @ts, @wh]    @equipment = []  end  def play()    intro()  end  # If I can find a way to pass arguments between scenes and game, I'm going to put a variable to change the starting point. Otherwise I know that both intro() and play() is useless.  def intro()    @city.intro()  endendclass Creature  attr_accessor :name, :life, :armor, :weapon, :regen  def initialize(name, level)    #Attributes accoridng to level and dice    @name = name    @level = level    @equipment = {:armor => 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 select_equipment(equipment)    introduce_self()    selection(equipment)    choice = gets.chomp.to_i    #@a and @w help to get the name of the array, armor or weapon.     if equipment[0].name == armor_list      wear_armor(equipment[choice])    elsif equipment[0].name == weapon_list      wear_weapon(equipment[choice])    else      raise ArgumentError, Should be armor_list or weapon_list    end    equipment.delete_at(choice)  end  def introduce_self()    if @equipment[:armor] == nil      puts You wear no armor!    else      puts You wear #{@equipment[:armor]}.    end    if @equipment[:weapon] == nil      puts You carry no weapon!    else      puts You carry #{@equipment[:weapon]}.    end   end        def selection(equipment)    puts You wanna some equipment?    for i in (1..equipment.length-1) do      puts #{i}. #{equipment[i].name}      i += 1    end  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 battle(opp1, opp2)    #a basic round system depending on even and uneven numbers    i = 1    while opp1.life > 0 && opp2.life > 0      if i % 2 == 0        attack(opp1, opp2)      elsif i % 2 == 1        attack(opp2, opp1)      else        #just learning to raise errors, not into rescue, yet        raise ArgumentError, The battle is over!      end      i += 1      round_result(opp1, opp2)    end  end  def round_result(opp1, opp2)    puts Hit points:    puts #{opp1.name}: #{opp1.life}    puts #{opp2.name}: #{opp2.life}  end  def attack(attacker, defender)    #this code below is just to prevent stuff like hit with -27 points of damage    possible_attack = @power + @weapon - defender.armor    if possible_attack > 0      attack = possible_attack    else      attack = 0    end    defender.life -= attack    puts #{attacker.name} hit #{defender.name} with #{attack} points of damage!    if defender.life <= 0      puts ...and killed!      defender.life = Dead as cold stone!      round_result(attacker, defender)      #game exits if one of the creatures die      Process.exit(0)    else      defender.life += defender.regen      puts #{defender.name} regenerates #{defender.regen} points of life!    end  endend#separate classes for weapons and armors, probably unnecessary but still learningclass Weapon  attr_reader :name, :power  def initialize(name, power)    @name = name    @power = power  endendclass Armor  attr_reader :name, :power  def initialize(name, power)    @name = name    @power = power  endend# I want each scene have its own weapon or armor (scattered on the ground, maybe, according to the story..) but cannot achieve that with armors, weapon variables. The same thing applies to monsters. I would like to have for example rats and thieves in the city, but bats and a dragon in the mountain. However couldn't achieve this exactly. So far I'm only successful in passing the game object to the scene object as the last variable and I think that's a good start.class Scene  attr_reader :name, :history, :armors, :weapons, :monsters  def initialize(name, history, armor_list, weapon_list, monsters, game)    @name = name    @history = history    @armor_list ||= []    @weapon_list ||= []    @monsters ||= []    @game = game  end  def intro()    puts You are in the  + @name + .    puts @history    choices()  end  def 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    #this is where things go really bad! instance_variable_get saves the battle but as for the equipment selection,    # as soon as I make a choice, it throws this error:    # game.rb:193:in 'choices': #<Armor:0x429720 @name=....> is not a symbol (TypeError)     # and I don't think that further addition of : or @ is needed here.    #The solution should be much simpler but couldn't find it on the web.    if choice == 1         @game.send(@game.instance_variable_get(:@h).select_equipment(@game.instance_variable_get(:@armor_list)))    elsif choice == 2         @game.send(@game.instance_variable_get(:@h).select_equipment(@game.instance_variable_get(:@weapon_list)))    elsif choice == 3         @game.send(@game.instance_variable_get(:@h).battle(@game.instance_variable_get(:@h), @game.instance_variable_get(:@d)))    elsif choice == 4      puts bad choice, since I am not ready yet!    else      puts Can't you read?    end  end  #this is just to show the player a list of equipment found in the scene.  def equipment_list()    @armor_list[1..@armor_list.length-1].each {|a| @equipment.push(a.name) }    @weapon_list[1..@weapon_list.length-1].each {|w| @equipment.push(w.name) }    puts You see some #{@room_equipment.join(, )} lying on the ground.  endendgame = Game.new()game.play()
Communication between two classes in ruby
object oriented;ruby;role playing game
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.rbclass 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]] }      ]  endend 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 defender.is_alive?    puts Hit points:    opponents.each { |o| puts #{o.name}: #{o.life} }    attack_turn = (attack_turn + 1) % 2    end    winner = opponents.first(&:is_alive?)    puts winner.name   endendclass Scene   attr_reader :monsters, :armors, :weapons  def initialize(setup)    @name = setup[:name]    @history = setup[:history]    @armors = setup[:armors].map { |x| Armor.new x }    @weapons = setup[:weapons].map { |x| Weapon.new x }    @monsters = setup[:monsters].map { |x| Creature.new x }  end  def intro()    puts You are in the  + @name + .    puts @history    equipment_list  end  def armor_picked(index)    @armors.delete_at(index)  end  def weapon_picked(index)    @weapons.delete_at(index)  end  def equipment_list()        (@armors + @weapons).each {|a| puts You see some #{a.name} lying on the ground. }    endendclass Creature  attr_reader :name, :life, :weapon, :armor  def initialize(config)    @name = config[:name]    @level = config[:level]    @equipment = {:armor => 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 endclass Weapon  attr_reader :name, :power  def initialize(setup)    @name = setup[:name]    @power = setup[:power]  endendclass Armor  attr_reader :name, :power  def initialize(setup)    @name = setup[:name]    @power = setup[:power]  endendgame = Game.new()game.play()