Using a method to generate multiple instances of another class - ruby

I am trying to let a method initiate multiple instances of another class.
class Players
def initialize(players)
#players = players
end
def generate_boards
#players.each do |player|
board = BingoBoardGenerator.new
player = BingoBoard.new(player, board.generate)
p player
end
end
end
players = ["Nick","Jiyoon","Mae","Lawson","Matthew"]
plays = Players.new(players)
plays.generate_boards
p player shows that five instances of BingoBoard were created properly, but I am not sure how to access them (or where they are). Any help on how to call these instances? Normally I would do:
nick = BingoBoard.new("Nick", board.generate)
nick.board
but when I instantiate them all together, I don't know how to set/access their instance name.

As indicated by user2864740, you can use :map instead of :each to return an array of BingoBoard instances. If you wanted to store those instances to use later, you can use memoization as shown below. The first time :bingo_board_instances is called, the boards will be generated and the instance variable #bingo_board_instances will be set so that future invocations of :bingo_board_instances will not result in the generation of new boards.
class Players
def initialize(players)
#players = players
end
def generate_boards
#players.map do |player|
board = BingoBoardGenerator.new
BingoBoard.new(player, board.generate)
end
end
def bingo_board_instances
#bingo_board_instances ||= generate_boards
end
end
While the above code works just fine, I think the more intuitive solution would be to have a Player class (instead of Players) and then pass in an array of Player instances when initializing a BingoBoardGenerator. With this approach, you can set an instance variable for each individual player and create a unique board that belongs to the player:
class BingoBoardGenerator
def initialize(args)
#dynamically set instance vars to handle n number of players
args.fetch(:players).each_with_index do |player,index|
instance_variable_set("#player_#{index+1}",player)
end
end
def generate_boards
instance_variables.map do |player|
player = instance_variable_get(instance_var)
#you will need to implement :generate_bingo_board on player...I would suggest using memoization so you can store the player's board for later retrieval
player.generate_bingo_board
end
end
end
#I have no idea what your implementation looks like...
b = BingoBoardGenerator.new(players: [Player.new("Jen"),Player.new("Phil"),Player.new("Mary"),Player.new("Bert")])
b.generate_boards
This would allow you to better encapsulate data that may belong to individual players, including the ability to ask each Player instance for its :board.

Related

Instantiating a class within a subclass and accessing its instance within another subclass

I have a class:
class Player
def initialize(name, npc=true)
#name = name
#npc = npc
end
end
and also a parent class and some subclasses:
class Scene
end
class Intro < Scene
joe = Player.new("Joe", false)
end
class NeighboursHouse < Scene
end
I create a new instance of Player within Intro, and I need to access that instance in NeighboursHouse too. Is there a way to do this without repeating in every subclass?
Just to clarify, I want to be able to create new Player instances within each Scene and access them in other scenes. They may be created at random based on differing outcomes so I won't necessarily be able to create them in the parent class.
This sounds like a program designing problem. If I were you, I would rather let the class Player to manage the players, and let the Scenes "invite" the players whenever it needs one.
class Player
#players = {}
def self.[](name, npc=true)
#players[name] ||= new(name, npc)
end
def initialize(name, npc=true)
#name = name
#npc = npc
end
end
And when you need a player, for example in Intro:
class Intro < Scene
joe = Player["Joe", false]
end
In this way, you don't need to worry about creating duplicated players.
The only setback is that there can't be 2 players with the same name but one is npc and the other is not.
The question is not clear, but I guess you can just create the instance in Scene, and refer to it from the subclasses.
class Scene
##joe = Player.new("Joe", false)
end
class Intro < Scene
##joe # => refers to the object
end
class NeighboursHouse < Scene
##joe # => refers to the same object
end
If the question is how to get some code snippet to run every time a class is inherited (but only once per inheritance), then you are looking for inherited:
class Scene
def self.inherited(subclass)
super
joe = Player.new("Joe", false)
end
end

Ruby find method callers class

I have classes A and B, each with methods a and b respectively, like below.
class A
def a
#I want to get the object which calls this method
#caller = B
end
end
class B
def initialize
#to_call = A.new
end
def b
#to_call.a
end
end
B.new.b
How can I return B that calls a so that I can use methods of B inside A?
I have a class Board, which Game classes use to play. The board class has a method interact that gets user input either with gets.chomp or STDIN.getc to simulate guessing games or games which use arrow keys. The game class calls the interact method to begin playing the game, and sends it a block that handles the way the game is played. Each game has its own set of rules, therefore each game class has a method that displays its rule book to the user. Within interact, when the user enters "-rules", I want the board class to return the class that called its interact method and store it in a variable caller. With the caller variable defined, I want to use caller.rule_book to display the rules of the game class that called the board's interact method.
I don't know a way you you could do it, besides using the source's location (caller_locations), but I'd advise not to follow this path. Too much indirection often leads to code extremely hard to debug. It's much simpler and readable to simply pass the context (in this case, self, or the properties needed for the computation) as an argument:
class A
def initialize(context)
#context = context
end
def a
# Do something with #context
end
end
class B
def initialize
#to_call = A.new(self)
end
def b
#to_call.a
end
end
B.new.b

How to count instances of class without counting reassignment?

I am working on class methods.
I am trying to count the number of created instances of a class. I am able to do this by creating a counter variable in the initialize method.
The problem arises when I reassign the variable originally assigned to one class instance. Because the initialize method is called twice, it does not recognize that the variable is simply being reassigned to another class instance.
class Ticket
attr_accessor :price
attr_reader :event, :venue
##count = 0
##tickets = {}
def initialize(event, venue)
#event = event
#venue = venue
##count += 1
end
def self.count
##count
end
end
a = Ticket.new("Michael Buble", "Staples")
a = Ticket.new("Frank Sinatra", "Madison Square Garden")
puts "Ticket count of #{Ticket.count}"
When I run the above code in IRB, it gives me a Ticket count of 2 (as expected). How do I change my code so that it recognizes the overwrite?
NOTE: I know that this question has been asked before for Objective C, but the reassignment aspect of the question adds a different element to the problem. Let me know otherwise.
ObjectSpace.each_object(Ticket).count
Will give you the count of object currently in memory. On testing in IRB I find it runs into the problem you describe, objects persist in memory even though you have assigned a new one to the variable. Technically the object still exists, even though you assign a new instance to the variable "a".
See this article: Deleting an object in Ruby The answers have plenty of info about what you are trying to do.
In the real world you wouldn't be counting instances in memory, you'd be asking a database how many exist. You need to think in terms of a database.
Your use of a to repeatedly contain the Ticket instance is wrong. You should be using an Array, Hash or Set to maintain the list, then ask the container how many exist:
require 'set'
class Ticket
attr_accessor :price
attr_reader :event, :venue
##tickets = Set.new
def initialize(event, venue)
#event = event
#venue = venue
##tickets << self
end
def delete
##tickets.delete(self)
end
def self.count
##tickets.size
end
end
a = Ticket.new("Michael Buble", "Staples")
b = Ticket.new("Frank Sinatra", "Madison Square Garden")
puts "Ticket count of #{Ticket::count}"
b.delete
puts "Ticket count of #{Ticket::count}"
You can build this out by adding ways to retrieve a particular instance from ##tickets, add a to_s so you can list them, but, in the end, you'll want to use a real database. If your code were to crash for any reason, your entire list of tickets would disappear, which would be unacceptable in real life.
If you really want to count live instances of the Ticket class (for reasons I cannot fathom), #Beartech has the right idea:
class Ticket
attr_reader :event, :venue
def initialize(event, venue)
#event = event
#venue = venue
end
def self.count_live_instances
ObjectSpace.garbage_collect
ObjectSpace.each_object(self).to_a.size
end
end
a = Ticket.new("Michael Buble", "Staples")
b = Ticket.new("Cher", "Canadian Tire Center")
a = Ticket.new("Frank Sinatra", "Madison Square Garden")
puts "Ticket instances count = #{Ticket.count_live_instances}" # => 2
It is essential to garbage-collect before invoking ObjectSpace#each_object. If you are skeptical, insert p ObjectSpace.each_object(self).to_a.size as the first line of self.count_live_instances. It will print 3.
(There is also a method ObjectSpace#count_objects. This method returns a hash like this one: {:TOTAL=>56139,..., :T_ARRAY=>3139,..., :T_ICLASS=>32}. Unfortunately, the keys are "object types"; you won't find :TICKET among them.)
class Gs
def self.method1
code...
end
def self.method2
code...
end
def self.method3
code...
end
end
Gs.new
p Gs.singleton_methods.count
The Gs.singleton_methods.count will print 3
it will count the singleton methods,if we use the self keyword or
classname.method name..

Testing methods that depend on an essentially random instance variable

I'm working on a Blackjack game. My Game object contains a deck object that gets shuffled after the deck reaches a certain level of penetration. Many of my methods depend on this deck object. I don't see any reason for the deck object to be accessible through setter methods. I'm having trouble testing the methods on my Game class, since they depend on the order of the deck, which is random.
For instance, I have the deal_hand method.
def deal_hand(player)
reset_deck if #deck.size < 2
player.hands.push(Hand.new(*#deck.pop(2)))
end
How should I test a method like this? I was thinking I could just manually create a Deck object that gets used in the #deck instance variable. Unfortunately, I can't set the instance variable, and I don't really want to add a setter, since there's no reason for that to be "settable" except to test. Should I monkey patch the class from my test-file and add a setter?
As an aside--I mostly write scripts--I decided I needed to start writing tests after this project got out of hand. Is there any canonical resource for "testing patterns?"
edit:
I am using MiniTest, which supports stubbing/mocking. Though as far as I can tell, it only lets you set expected return values for method calls on the mock object. If I made a Mock deck, the actual deck object also depends on an internal array. None of the code that calls the deck accesses the array directly.
Use a mock library. RSpec comes built with one, but I dislike it, so I'll show you what it might look like with Surrogate, the one I wrote:
class Deck
def pop(n) end
def reset() end
def size() end
end
class Game
def initialize(deck)
#deck = deck
end
def deal_hand(player)
reset_deck if #deck.size < 2
player.hands.push(Hand.new(*#deck.pop(2)))
end
def reset_deck
#deck.reset
end
end
Hand = Struct.new :card1, :card2
class Player
def hands
#hands ||= []
end
end
require 'surrogate/rspec'
class MockDeck
Surrogate.endow self
define(:reset)
define(:pop) { |n| n.times.to_a }
define(:size) { 1 }
end
describe Game, 'deal_hand' do
let(:deck) { MockDeck.new }
let(:player) { Player.new }
let(:game) { Game.new deck }
it 'resets the deck if there are less than 2 cards' do
deck.will_have_size 2 # set the return value of deck.size
game.deal_hand player
deck.was_not told_to :reset # assert what happened to the deck
deck.will_have_size 1
game.deal_hand player
deck.was told_to :reset
end
it 'deals the top 2 cards to the player' do
deck.will_pop [:card1, :card2]
game.deal_hand player
deck.was told_to(:pop).with(2)
player.hands.last.should == Hand.new(:card1, :card2)
end
end
describe Deck do
it 'is substitutable for the mock' do
# because we use the mock in tests
# we want to make sure its interface matches the real deck
Deck.should substitute_for MockDeck
end
end
Have you considered using mocha?
That would allow you to stub or mock the Deck to ensure that it has the expected cards for your test runs.
In your test use the method instance_variable_set, which is a ruby method on object.
So I am assuming your method is in the Game class, so you if you're setting up something like
#test_deck = something_that_sets_up_state_of_test_deck
#game = Game.new
#game.instance_variable_set(:deck, #test_deck
That will set your instance variable within Game, without the need for attr_accessible or getters and setters being explicitly built.

Can one instance variable access another in the same class?

I am writing a chess engine in Ruby.
I have a Game class, which consists of two attributes:
:board, an instance of my Board class.
:log, an array of moves, for saving & loading games.
The Board class consists of two attributes:
:white, an instance of my Player class.
:black, an instance of my Player class.
The Player class consists of piece locations, represented as bitstrings:
:pawns, :knights, ..., :king
I would like the Player class to have methods like in_check? to indicate if that player is in check. However, that requires, #white to know the values of #black, which is an instance back in the Board class.
Is there a way I can access the variable #black from #white without explicitly passing the locations of the black pieces as a parameter to the in_check method?
The first player can pass message "you're in check" to another player after or before making a step.
The accessing variables is not a good idea, it leads to the future problems, pass messages instead.
Let #white and #black respectively ask the board about the check.
Update:
Well, I guess there are a lot of assumptions here on my side, so lets write these down and see whether we have some common ground:
class Board
def initialize
...
#black = Player.new(self, other_args*)
#white = Player.new(self, other_args*)
...
end
def am_i_in_check?(player)
case player
when #black
return does_white_check_black?
when #white
return does_black_check_white?
end
end
...
end
class Player
...
def initialize(board,...)
...
#board=board
...
end
...
def wants_to_know_whether_it_is_checked
#board.am_i_in_check?(self)
...
end
...
end
I guess there are typos hidden above, but it should describe my idea now.

Resources