Reputation: 11810
I am making a card game in ruby.
I have the Game class, which has an array of Player objects.
array_of_players = Array[
Player.new("Ben"),
Player.new("Adam"),
Player.new("Peter"),
Player.new("Fred"),
]
my_game = Game.new(array_of_players)
puts my_game.players[2].name #=> Peter
Each player also has access to the Game, so that they can access the important bits of the game like so
self.game.last_card_dealt
Each player also has cards (Player.cards), and I want to make sure that players can't access each other's cards. However, the Game does need access to the cards, so I don't think using private
is appropriate, and the players need access to some of each other's information, so I don't think I want that to be private
either...
Basically, I want these to work.
self.cards #where self is a Player object
self.players[0].cards #where self is the Game
self.game.players[0].name #where self is a Player object
And this to fail:
self.hand.players[0].cards #=> Nice try sucker! Cheating is for losers.
How are more complex permissions like this handled? Thanks.
Upvotes: 4
Views: 817
Reputation: 11810
Thanks for all your responses.
In the end I figured that I could give the authorised object a key which is used to allow it access to the meat of a method.
Game object has @auth_object and sets it to the player object it intends to access the secret methods of, and the player secret method checks if hand.auth_object is self
, otherwise it does nothing. Then @auth_object is set back to nil. There is an attr_reader but no writer for @auth_object.
That works.
Upvotes: 1
Reputation: 1417
This is more practical than my other answer, and uses the Game object as a delegate to all information in the game itself (Players, Cards, etc.). Note that you still have to trust the caller to pass themselves, but seriously where do you draw the line?
class Player
attr_reader :name
def initialize(name)
@name = name
end
end
class Cards
attr_accessor :cards
end
class Game
attr_reader :name, :players
def initialize(players)
@name = "Game Master"
@hands = []
@players = players.each do |p|
puts "Added %s to game." % p.name
@hands << {:player => p, :cards => Cards.new}
end
end
def view_hand(player, caller)
@hands.each do |hand|
if hand[:player] == player
if hand[:player] == caller or caller == self
puts "%s: You can access all these cards: %s" % [caller.name, hand[:cards]]
else
# Do something to only display limited cards depending on this caller's view capabilities
puts "%s: You can only access the cards I will let you see: %s" % [caller.name, hand[:cards]]
end
end
end
end
def my_cards(player)
@hands.each do |hand|
puts "%s's cards: %s" % [player.name, hand[:cards]] if hand[:player] == player
end
end
end
g = Game.new([Player.new('Bob'), Player.new('Ben')])
puts "\nCalling each Player's cards as each Player:\n\n"
g.players.each do |gp|
g.players.each do |p|
g.view_hand(gp, p)
end
end
puts "\nCalling each Player's cards as Game:\n\n"
g.players.each do |p|
g.view_hand(p, g)
end
puts "\nEach Player calls for their own cards:\n\n"
g.players.each do |p|
g.my_cards(p)
end
The output:
Added Bob to game.
Added Ben to game.
Calling each Player's cards as each Player:
Bob: You can access all these cards: #<Cards:0x100121c58>
Ben: You can only access the cards I will let you see: #<Cards:0x100121c58>
Bob: You can only access the cards I will let you see: #<Cards:0x100121bb8>
Ben: You can access all these cards: #<Cards:0x100121bb8>
Calling each Player's cards as Game:
Game Master: You can access all these cards: #<Cards:0x100121c58>
Game Master: You can access all these cards: #<Cards:0x100121bb8>
Each Player calls for their own cards:
Bob's cards: #<Cards:0x100121c58>
Ben's cards: #<Cards:0x100121bb8>
Upvotes: 3
Reputation: 1417
This was fun to play with. I'm not sure if this is the best possible answer, but it works. The key is to pass the calling object to Player.cards(obj), and check if it's either the Player itself, or of type Game, both of which have legal access.
class Player
attr_accessor :name, :game
attr_writer :cards
def initialize(name)
@name = name
@game = nil
@cards = nil
end
def cards(caller)
puts "%s cards called by %s." % [self, caller]
if caller.kind_of?(Game) or caller == self
puts "Here's your cards %s." % @cards
else
puts "Nice try sucker! Cheating is for losers."
end
end
end
class Cards
def initialize
@cards = [1, 2, 3]
end
end
class Game
attr_reader :players
def initialize(players)
@players = players.each do |p|
puts "Added %s to game." % p.name
p.game = self
p.cards = Cards.new
end
end
end
g = Game.new([Player.new('Bob'), Player.new('Ben')])
puts "\nCalling each Player's cards as each Player:\n\n"
g.players.each do |gp|
g.players.each do |p|
p.cards(gp)
end
end
puts "\nCalling each Player's cards as Game:\n\n"
g.players.each do |p|
p.cards(g)
end
And the output:
Added Bob to game.
Added Ben to game.
Calling each Player's cards as each Player:
#<Player:0x100122b30> cards called by #<Player:0x100122b30>.
Here's your cards #<Cards:0x1001229c8>.
#<Player:0x100122ae0> cards called by #<Player:0x100122b30>.
Nice try sucker! Cheating is for losers.
#<Player:0x100122b30> cards called by #<Player:0x100122ae0>.
Nice try sucker! Cheating is for losers.
#<Player:0x100122ae0> cards called by #<Player:0x100122ae0>.
Here's your cards #<Cards:0x100122928>.
Calling each Player's cards as Game:
#<Player:0x100122b30> cards called by #<Game:0x100122ab8>.
Here's your cards #<Cards:0x1001229c8>.
#<Player:0x100122ae0> cards called by #<Game:0x100122ab8>.
Here's your cards #<Cards:0x100122928>.
Upvotes: 2
Reputation: 77826
Keep Game.player
private to disallow players from accessing other players through the array.
e.g., When self
is a player, self.game.players[0].name
is kind of silly.
Perhaps you'd like a public Game.player_names
method that just returns an array of player names?
On top of that, you could make a public Players.opponents
method.
class Game
# ...
def player_names
self.players.collect { |p| p.name }
end
private
# private game methods
end
class Player
# ...
def opponents(i=nil)
return i.nil? ? self.game.player_names : self.game.player_names[i]
end
end
Upvotes: 2