How do I pass variables between Ruby classes? - ruby

I'm creating a card game with multiple classes. Currently, I'm using global variables to hold the $shuffled_deck, $players_hand, and $dealers_hand variables, but I worry when using global variables (perhaps, needlessly) and would prefer to use instance variables.
I've been reading around, but nothing is really clicking. Can anyone help point me in the right direction with this?
Using instance variables I haven't been able to save the #players_hand and #dealers_hand to be able to use them in other classes. For instance, I have #players_hand from the Player class. I have the Dealer class draw a card, but I can't pull that #players_hand into the Dealer class to add the two together.
My current code is:
class Blackjack
def initialize
#player = Player.new
#dealer = Dealer.new
end
end
class Dealer
def initialize
#deck = Deck.new
$dealers_hand = 0
end
def hit_dealer
#deck.hit_dealer
end
def hit_player
#deck.hit_player
end
def draw_card
#hit = $shuffled_deck
end
def shuffle
#deck.suits
end
end
class Player
def initialize
$players_hand = 0
end
end
class Deck
def suits
#code that shuffled the deck..
$shuffled_deck = #shuffled_deck
end
def hit_player
#hit = $shuffled_deck.pop
end
def hit_dealer
#hit = $shuffled_deck.pop
end
end

using your example you can do it like this
class Blackjack
attr_reader :player, :dealer
def initialize
#player = Player.new
#dealer = Dealer.new
end
end
class Dealer
def dealers_hand #the long java way of a getter
#dealers_hand
end
#and now the short ruby way
attr_reader :dealers_hand #if you only need to read the attribute
attr_writer :dealers_hand #if you only need to write the attribute
attr_accessor: dealers_hand #if you need both
def initialize
#deck = Deck.new
#dealers_hand = 5
end
def hit_dealer
#deck.hit_dealer
end
def hit_player
#deck.hit_player
end
def draw_card
#hit = $shuffled_deck
end
def shuffle
#deck.suits
end
end
class Player
attr_reader :players_hand
def initialize
#players_hand = 0
end
end
class Deck
def suits
attr_reader :shuffled_deck
#shuffled_deck = #shuffled_deck
end
def hit_player
#hit = $shuffled_deck.pop
end
def hit_dealer
#hit = $shuffled_deck.pop
end
end
game = Blackjack.new
p game.dealer.dealers_hand
game.dealer.dealers_hand = 4
p game.dealer.dealers_hand

You want to use attr_reader, attr_writer, or attr_accessor. Here's how they work:
attr_reader :players_hand: Allows you to write some_player.players_hand to get the value of that player's players_hand instance variable
attr_writer :players_hand: Allows you to write some_player.players_hand = 0 to set the variable to 0
attr_accessor :players_hand: Allows you to both read and write, as though you'd used both attr_reader and attr_writer.
Incidentally, all these do is write methods for you. If you wanted, you could do it manually like this:
class Player
def initialize
#players_hand = 0
end
def players_hand
#players_hand
end
def players_hand=(new_value)
#players_hand = new_value
end
end

Related

How to access an instance variable in another Ruby class?

class Player
attr_accessor :card_pile
def initialize
#bust = false
#card_pile = []
end
def bust?
return #cards.inject(:+) > 21
end
end
I have this Player class and have initazlied card_pile variable
class Game
def initialize
#players = []
end
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
puts #players
player = Player.new
player.initialize
while #card_pile.length < 2 do
new_card = Card.new
#card_pile.push(new_card.value)
end
end
I wish to use this variable in the while loop below. Why cannot this be accessed in the way I am hoping it will be?
The error message is: ``playing_game': private method initialize' called for #<Player:0x007fda53073f48 #bust=false, #card_pile=[]> (NoMethodError)
initialize is called automatically when you make a new instance of a class using Player.new. You don't currently have any arguments being passed in to your initialize method, but you have set the instance variable card_pile with attr_accessor, so you can do this:
player = Player.new
while player.card_pile.length < 2 do
new_card = Card.new
player.card_pile.push(new_card.value)
end

How to get all instances variables in ruby class?

I have a ruby class, and in one of the methods, it calls an external function, and pass in all instance variables, and continue with the return value. Here is the code:
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = ???? # how to collect all my instance variables into a dict/Hash?
res = out_func(all_vars)
do_more_stuff(res)
end
end
The problem is the instance variables might vary in subclasses. I can't refer them as their names. So, is there a way to do this? Or Am I thinking in a wrong way?
You can use instance_variables to collect them in an Array. You will get all initialized instance variables.
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = instance_variables
res = out_func(all_vars)
do_more_stuff(res)
end
end
You could keep track of all accessors as you create them:
class Receiver
def work(arguments)
puts "Working with #{arguments.inspect}"
end
end
class MyClass
def self.attr_accessor(*arguments)
super
#__attribute_names__ ||= []
#__attribute_names__ += arguments
end
def self.attribute_names
#__attribute_names__
end
def self.inherited(base)
parent = self
base.class_eval do
#__attribute_names__ = parent.attribute_names
end
end
def attributes
self.class.attribute_names.each_with_object({}) do |attribute_name, result|
result[attribute_name] = public_send(attribute_name)
end
end
def work
Receiver.new.work(attributes)
end
attr_accessor :foo
attr_accessor :bar
end
class MySubclass < MyClass
attr_accessor :baz
end
Usage
my_class = MyClass.new
my_class.foo = 123
my_class.bar = 234
my_class.work
# Working with {:foo=>123, :bar=>234}
my_subclass = MySubclass.new
my_subclass.foo = 123
my_subclass.bar = 234
my_subclass.baz = 345
my_subclass.work
# Working with {:foo=>123, :bar=>234, :baz=>345}

Ruby add initialized object to class array

I am unable to figure out or find any information on how to push the initialized object pointer to an array accessed from a class level variable. Here is an example.
Class Color
##colors = Array.new
def initialize
##colors << red
end
def self.list
##colors.each do |color|
puts color.to_hex
end
end
end
red = Color.new
Thanks guys for your help.
I would do it this way:
class Color
#colors = []
def self.new(*args, &blk)
#colors << super
end
def self.list
puts #colors.map(&:to_hex)
end
end
red = Color.new
Color.list
Personally, I feel uncomfortable doing class-level stuff in the instance initializer, it just doesn't feel right. The class is a completely independent object, having the instance know too much about the class smells of bad OO.
You can use self to reference the current instance of the class:
class Color
##colors = Array.new
def initialize
##colors << self
end
def self.list
##colors.each do |color|
puts color.to_hex
end
end
end
You should prefer class instance variables over class variables. Class variables are like globals - if you change it in a subclass it will also change the variable in the superclass. This is rarely the wanted effect. Here's #JKillian's code rewritten with class instance variables:
class Color
class << self
attr_accessor :colors
end
#colors = Array.new
def initialize
Color.colors << self
end
def self.list
#colors.each do |color|
puts color.to_hex
end
end
end

how to get (and set) variables inside a ruby class?

I update this question to better reflect what I have problems to grasp. The example below kind of work but how can I access the Sub class then I have defined it inside the Base class? Should it not be better to do the call outside the class? If so how do I do that? The second question I have in this example is how to grab values so I can use them in another class. Here I store the values in an array that I later need to unpack in another class. Should I not be able to use a proc for this?
Basically what I want to do is to sort the methods into two different classes depending on if they are nested or not.
class Sub
def initialize(base_class_method)
#base_class_method = base_class_method
#sub_methods = []
end
# omitted code here
def base_class_method
#base_class_method
end
def sub_actions(method)
#sub_methods << method
end
def return_sub_methods
#sub_methods
end
def method_missing(sub_method, &block)
if sub_method
sub_method
else
super
end
end
end
class Base
def initialize
#base_methods = []
end
# omitted code here
def base_actions(method)
#base_methods << method
end
def return_base_methods
#base_methods
end
def method_missing(method, &block)
if block_given?
Sub.new(method).instance_eval(&block)
elsif method
base_actions(method)
else
super
end
end
end
base = Base.new
base.instance_eval do
something1
something_with_a_block do
something_inside_block1_1
something_inside_block1_2
end
something2
something_with_a_block2_2 do
something_inside_block2_1
end
end
p base.return_base_methods #=> [:something1, :something2] works!
You can do something like this.
class Test
# reserved method to instantiate object
def initialize(a,b,c)
#a = a
#b = b
#c = c
end
# getters
def a
#a
end
def b
#b
end
def c
#c
end
def abc
[#a, #b, #c] # returns an array
end
# setters
def a=(var)
#a = var
end
def b=(var)
#b = var
end
def c=(var)
#c = var
end
# set values all at once
def update(a, b, c)
#a = a
#b = b
#c = c
end
end
z = Test.new('something','something','something')
z.update('something!','nothing!',"a thing!")
z.a
z.b
z.c
z.a = 'wow, new value!'

Can a variable be changed within a block?

Let's go to the code directly:
#!/usr/bin/ruby
require 'tk'
class Epg
def initialize
#var = "bad"
#cvs = nil
#items_demo = TkRoot.new() {title "EPG"}
TkFrame.new(#items_demo) {|cf|
#var = "good"
#cvs = TkCanvas.new(cf) {|c|}
puts "#cvs 1 is #{#cvs}"
puts "#var 1 is #{#var}"
}.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
puts "#cvs 2 is #{#cvs}"
puts "#var 2 is #{#var}"
end #initialize
def test
#var = "bad"
puts " #var 3 :#{#var}"
(1..3).each {|x| #var="good"}
puts " #var 4 :#{#var}"
end
end
e= Epg.new
e.test
Here is the output:
#cvs 1 is #<Tk::Canvas:0xb7cecb08>
#var 1 is good
#cvs 2 is
#var 2 is bad ##var has NOT been changed by the code in the block
#var 3 :bad
#var 4 :good ##var has been changed by the code in the block
Why we see different behavior here?
You can think of blocks as closing over both the set of local variables and the current self.
In Ruby, you will always have access to local variables, no matter what. The self encapsulates instance methods on the current object as well as instance variables.
Consider the following code:
class Table
def initialize(legs)
#legs = legs
end
def with_legs
yield #legs
end
end
And then:
def some_calling_method
name = "Yehuda"
Table.new(4) {|legs| puts "#{name} gnaws off one of the #{legs} legs" }
end
By Ruby's block semantics, you can be assured that name will be available inside the block, even without looking at the method you're calling.
However, consider the following:
class Person
def initialize(name)
#name = name
end
def gnaw
Table.new(4).with_legs do |legs|
puts "#{#name} gnaws off one of the #{legs} legs"
end
end
end
Person.new("Yehuda").gnaw
In this case, we are accessing the #name instance variable from inside the block. It works great in this case, but is not guaranteed. What if we implemented table a bit differently:
class Table
def initialize(legs)
#legs = legs
end
def with_legs(&block)
self.instance_eval(&block)
end
end
Effectively, what we're saying is "evaluate the block in the context of a different self." In this case, we are evaluating the block in the context of the table. Why would you do that?
class Leg
attr_accessor :number
def initialize(number)
#number = number
end
end
class Table
def initialize(legs)
#legs = legs
end
def with_leg(&block)
Leg.new(rand(#legs).instance_eval(&block)
end
end
Now, you could do:
class Person
def initialize(name)
#name = name
end
def gnaw
Table.new(4).with_leg do
puts "I'm gnawing off one of leg #{number}"
end
end
end
If you wanted access to the person object inside of the block, you'd have to do:
class Person
def initialize(name)
#name = name
end
def gnaw
my_name = name
Table.new(4).with_leg do
puts "#{my_name} is gnawing off one of leg #{number}"
end
end
end
As you can see, the use of instance_eval can make it simpler and less bulky to access methods of a far-off object inside a block, but comes at the cost of making the self unaccessible. The technique is usually used in DSLs, where a number of methods are injected into the block, but the self doesn't matter that much.
This is what's happening with Tk; they're using instance_eval to inject their own self into the block, which is wiping your self clean.
The explanation is that TkFrame.new uses instance_eval and thus the assignment #var = "good" changes the instance variable of TkFrame. Try this out:
class A
def initialize(&b)
instance_eval(&b)
end
end
class B
def initialize
#x = 10
#a = A.new do
#x = 20
end
end
end
p B.new
This is what you'll see:
#<B:0x10141314 #x=10, #a=#<A:0x10141300 #x=20>>

Resources