How to make 2 entities of the same class interact? - ruby

I am trying to get 2 "people" of the same class to interact with each other, and change each other's "stats." However, I get the error, "formal argument cannot be a constant."
class Hands
attr_reader :name, :element, :skill, :mana, :health, :attack, :fire, :water, :lyfe
def initialize(name, element, skill)
#mana = 100
#health = 200
#name = name
#element = element
#skill = skill
end
def mana
sleep 1
puts "#{#name} has #{#mana} mana."
end
def health
sleep 1
puts "#{#name} has #{#health} HP."
end
def restore(Hands)
if #element == "Lyfe"
#mana = 100
puts "#{#name} has been restored!"
else
puts "#{#name} cannot use this ability!"
end
end
end
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player2 = Hands.new('Nubz', 'Lyfe', 'Manipulate Wildlife')
player3 = Hands.new('Lisk', 'Water', 'Invisible')
player4 = Hands.new('Azzi', 'Water', 'Manipulate Water')
player5 = Hands.new('Zeph', 'Fire', 'Lightning')
player6 = Hands.new('Ford', 'Fire', 'Manipulate Fire')
player7 = Hands.new('Boyd', 'Fire', 'Craft')
player1.restore("Nubz")
Here I am trying to get player1 to "restore" player2 back to full mana.
I know the code isn't perfect, but I'm not sure how else to do this.
Focussing on the restore command, the others work fine.

You should be able to pass the instance of the player you want affected to the restore method rather than the name string. You can then update that player's attributes as needed. A quick example:
Update class to make mana writeable:
class Hands
attr_reader :name, :element, :skill, :mana, :health, :attack, :fire, :water, :lyfe
attr_writer :mana
# ...
end
Update restore method to accept player instance:
def restore(hands)
if element == "Lyfe"
hands.mana = 100
puts "#{hands.name} has been restored!"
else
puts "#{name} cannot use this ability!"
end
end
Pass instance to method:
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player2 = Hands.new('Nubz', 'Lyfe', 'Manipulate Wildlife')
player1.restore(player2)
#=> Nubz has been restored!

Related

Getting parts of two classes to interact with a random chance?

I know I'm asking a lot of questions, and I apologize for that.
I am trying to get 2 classes to interact with each other, but with a random chance.
class Hands
attr_reader :name, :element, :skill, :mana, :health, :attack, :fire, :water, :lyfe, :summons
attr_writer :mana, :health
attr_accessor :summon
def initialize(name, element, skill)
#mana = 100
#health = 200
#summons = []
#name = name
#element = element
#skill = skill
end
def summon
#summons << summon
random_number = [1, 2].sample
if #element == "Lyfe"
if random_number == 1
puts "#{#name} summoned #{summon1.name}"
elsif random_number == 2
puts "#{#name} summoned #{summon2.name}"
else
puts "#{#name} can not use this ability!"
end
end
end
end
class Summons
attr_reader :name, :strength, :health
attr_writer :name, :strength, :health
attr_accessor :summon
def initialize(name, strength)
#name = name
#strength = strength
if #strength == "1"
#health = 25
#mana = 25
elsif #strength == "2"
#health = 50
#mana = 50
elsif #strength == "3"
#health = 100
#mana = 75
end
end
end
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player2 = Hands.new('Nubz', 'Lyfe', 'Manipulate Wildlife')
player3 = Hands.new('Lisk', 'Water', 'Invisible')
player4 = Hands.new('Azzi', 'Water', 'Manipulate Water')
player5 = Hands.new('Zeph', 'Fire', 'Lightning')
player6 = Hands.new('Ford', 'Fire', 'Manipulate Fire')
player7 = Hands.new('Boyd', 'Fire', 'Craft')
summon1 = Summons.new('Berto', '1')
summon2 = Summons.new('Wicket', '1')
summon3 = Summons.new('Skye', '1')
summon4 = Summons.new('Test4', '2')
summon5 = Summons.new('Test5', '2')
summon6 = Summons.new('Test6', '3')
player1.summon
I know I probably have some unnecessary code in there.
I am trying to get one of the players (Hands class) to randomly "summon" one of the summons. I tried using a random_number.sample command, but I got a stack level too deep error. The random_number code is shortened to 1 & 2 for testing purposes, but if it can somehow work and expand to all 6 that would be great.
Any explanation would help!
I got a stack level too deep error
That's because you call summon from within summon:
def summon # <--+
#summons << summon # |
# ^^^^^^------+ invokes itself over and over again
But let's start from the beginning. The Player#summon method needs to somehow access the "summons".
You could create an array of available summons, e.g.
available_summons = [
Summons.new('Berto', '1'),
Summons.new('Wicket', '1'),
Summons.new('Skye', '1')
]
And then pass that array to your summon method:
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player1.summon(available_summons)
Within your method you would sample from that array and add the sampled element to the player's #summons array: (I removed any additional logic for now)
def summon(available_summons)
summon = available_summons.sample
#summons << summon
puts "#{#name} summoned #{summon.name}"
end
Example with output:
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player1.summon(summons)
# Silk summoned Berto
player1.summon(summons)
# Silk summoned Skye
The #summons array will be populated as expected:
player1.summons
#=> [
# #<Summons:0x00007fbf1c9d80b8 #name="Berto", #strength="1", #health=25, #mana=25>,
# #<Summons:0x00007fbf1c9189c0 #name="Skye", #strength="1", #health=25, #mana=25>
# ]
Note that passing the array to Player#summon is just one possible solution. You could also pass it to Player.new (i.e. initialize) and store it in an instance variable. Or – if the array is rather static – you could store it right within the Player class, maybe in a constant. It really depends on how you want to structure your code.

Using a variable across classes in Ruby

I have this class:
class Player
attr_accessor :card_pile, :name
def initialize
#name = name
#bust = false
#card_pile = []
end
def bust?
return #cards.inject(:+) > 21
end
end
I also have this as the beginning of another class
def playing_game
puts "How many players are playing? "
players_amount = gets.chomp.to_i
(0...players_amount).each do
puts ("What is the players name? ")
#name = gets.chomp
#players.push(#name)
end
#players.each do |each_player|
#name = Player.new
while true
while #name.card_pile.length < 2 do
new_card = Card.new
#name.card_pile.push(new_card.value)
end
puts(#name.card_pile)
print #name, "'s turn" "\n"
At the moment this will print out #<Player:0x007fc14984a4b0>'s turn instead of Rich's turn
Why is this happening? I thought I had made an instance variable in the Player class and then instantiated this class #name = Player.new and then could reference it from here on out??
This will help
def playing_game
puts 'How many players are playing?'
players_amount = gets.chomp.to_i
players_names = (0...players_amount).map do
puts ("What is the players name? ")
gets.chomp
end
players_names.each do |player_name|
player = Player.new(player_name)
while player.card_pile.length < 2 do
new_card = Card.new
player.card_pile.push(new_card.value)
end
puts player.card_pile
puts "#{player.name}'s turn"
end
end
UPD:
You don't need instance variables (like #name and #players inside single method).
In this code you iterate over players names
#players.each do |each_player|
=>
players_names.each do |player_name|
In context of
#name = Player.new
name is a Player instance
to create player with given name pass it to initializer:
player = Player.new(player_name)
then call name on Player instance, that you create earlier
puts "#{player.name}'s turn"

obj.method(argument)

I am looking at this code:
class Student
attr_accessor :first_name, :last_name, :age
def initialize(first, last, age)
#first_name = first
#last_name = last
#age = age
end
def birthday
#age += 1
end
end
class ViewStudent
def initialize(student)
#student = student
end
def do_something
puts "Student name: #{#student.first_name} #{#student.last_name}"
end
end
class UpdateStudent
def initialize(student)
#student = student
end
def do_something
puts "What is the student's first name?"
#student.first_name = gets.chomp
puts "What is the student's last name?"
#student.last_name = gets.chomp
puts "Updated student: #{#student.first_name} #{#student.last_name}"
end
end
choices = [ViewStudent, UpdateStudent]
student = Student.new("John", "Doe", 18)
puts "Select 1 to view student or 2 to update student."
selection = gets.chomp.to_i
obj = choices[selection - 1]
obj = obj.new(student)
obj.do_something
In the last five lines, I understand that selection = gets.chomp.to_i converts the selection options to integers, but how does that work in tandem with obj = choices[selection - 1]?
I'm also not sure what obj = obj.new(student) and obj.do_something do. It looks like a local variable is being set to create a new object with student as the argument. However, obj isn't a class or method to call on?
I can also gather that obj.do_something calls the methods defined for both ViewStudent and UpdateStudent given the selection.
I saw this, but it doesn't answer my question.
obj = choices[selection - 1] just select ViewStudent if 1 and UpdateStudent if 2 from your array by index (choices[0] or choices[1]).
Then you creating an instance of selected class (ViewStudent.new or UpdateStudent.new) and call do_something method on this instance, because this methos difined in both classes:
obj = choices[selection - 1] # obj is ViewStudent or UpdateStudent class
obj = obj.new(student) # obj is ViewStudent or UpdateStudent instance
obj.do_something # call `do something` method on instance

How can I combine two blocks to simplify my code?

Hi I am a student learning Ruby. I am using the quick start guide at ruby-lang.org, which has some examples of Ruby basics.
I studied the MegaGreeter class, and I am trying to figure out how to puts two arguments (name and age) in the same each block in order to simplify my code.
I think there would be another way. (Using regular loops instead of each.)
Calculate the array's size.
Use a loop like in C.
But I want to use the each loop. Below is my code:
class MegaGreeter
attr_accessor :name
attr_accessor :age
#Creat the object
def initialize(name=nil, age=0)
#name = name
#age = age
#tmp = Array.new()
#i = 0
end
#Say hi to everybody
def say_hi
if #name.nil?
puts "please give me the input !!"
elsif #name.respond_to?("each")
#list responding
#name.each do |name|
#tmp[#i] = "hi ~! #{name}"
#i += 1
end
#i=0
#age.each do |age|
#tmp[#i] += " and you are #{age} years old"
puts #tmp[#i]
#i += 1
end
else
puts "give me array ~"
end
end
end
a = MegaGreeter.new()
a.name = ["juno","yoonhe"]
a.age = [1,2]
a.say_hi
You can use the Array method zip to first combine your two arrays. It groups the elements by their position in the array, so the first element of the #name array will be grouped with the first element of the #age array and so on.
#name = ['Foo', 'Bar']
#age = [23, 41]
name_and_age = #name.zip(#age)
# [['Foo', 23], ['Bar' 41]]
Now the names and ages are grouped together, and you can iterate over them using each.
name_and_age.each do |name, age|
puts name, age
end
# Foo 23
# Bar 41
Putting it back into your original code:
class MegaGreeter
attr_accessor :name, :age
#Creat the object
def initialize(name = nil, age = 0)
#name = name
#age = age
end
#Say hi to everybody
def say_hi
if #name.nil?
puts "please give me the input !!"
elsif #name.respond_to?("each")
#list responding
#name.zip(#age).each do |name, age|
puts "hi ~! #{name} and you are #{age} years old"
end
else
puts "give me array ~"
end
end
end

Ruby: Using variables between classes

I am making a short, text-based game as an extra credit exercise based on the Ruby I have learned so far and I'm having trouble getting classes to read and write variables between each other. I have read extensively and searched for clarification on how to do this but I haven't had much luck. I have tried using # instance variables and attr_accessible but I can't figure it out. Here is my code so far:
class Game
attr_accessor :room_count
def initialize
#room_count = 0
end
def play
while true
puts "\n--------------------------------------------------"
if #room_count == 0
go_to = Entrance.new()
go_to.start
elsif #room_count == 1
go_to = FirstRoom.new()
go_to.start
elsif #room_count == 2
go_to = SecondRoom.new()
go_to.start
elsif #room_count == 3
go_to = ThirdRoom.new()
go_to.start
end
end
end
end
class Entrance
def start
puts "You are at the entrance."
#room_count += 1
end
end
class FirstRoom
def start
puts "You are at the first room."
#room_count += 1
end
end
class SecondRoom
def start
puts "You are at the second room."
#room_count += 1
end
end
class ThirdRoom
def start
puts "You are at the third room. You have reached the end of the game."
Process.exit()
end
end
game = Game.new()
game.play
I want to have the different Room classes change the #room_count variable so that Game class knows which room to go to next. I am also trying to do this without implementing class inheritance. Thanks!
class Room
def initialize(game)
#game = game
#game.room_count += 1
end
def close
#game.room_count -= 1
end
end
class Game
attr_accessor :room_count
def initialize
#room_count = 0
end
def new_room
Room.new self
end
end
game = Game.new
game.room_count # => 0
room = game.new_room
game.room_count # => 1
room.close
game.room_count # => 0

Resources