A nil class when trying to initialize a deck of cards - ruby

The following code is resulting in nil and I can't figure out why. Is there something wrong with my initialization?
class Card
VALUES = %w(2 3 4 5 6 7 8 9 10 J Q K A)
SUITS = %w(S H D C)
def initialize(suit, value)
#suit = suit
#value = value
end
end
class Deck
attr_accessor :cards
def initialize
#cards = []
Card::SUITS.each do |suit|
Card::VALUES.each do |value|
#cards << Card.new(suit, value)
end
end
end
end
Deck.new
p #cards

#cards is not known outside the object. Outside the class Deck it is a instance variable of the top-level scope in Ruby.
You have to use the accessor method to get the content:
class Card
VALUES = %w(2 3 4 5 6 7 8 9 10 J Q K A)
SUITS = %w(S H D C)
def initialize(suit, value)
#suit = suit
#value = value
end
end
class Deck
attr_accessor :cards
def initialize
#cards = []
Card::SUITS.each do |suit|
Card::VALUES.each do |value|
#cards << Card.new(suit, value)
end
end
end
end
deck = Deck.new #<--- Store object in a variable
p deck.cards #<--- Use accessor

or just:
my_deck = Deck.new
p my_deck.cards

In short, instance variables can only be seen by other methods from within the same class. I believe what you're trying to do is:
new_deck = Deck.new
p new_deck.cards
Calling the cards method on new_deck returns #cards.

You create a new object Deck.new, but you don't print the value of its cards - you print a #cards instance variable which in this context is nil.
You probably wanted something like p Deck.new.cards.

Currently your output for #cards array is difficult to read and contains object info. I thought I'd offer an alternative I've just conjoured up, hope it helps:
class Deck
attr_writer :suits, :values
attr_accessor :deck
def initialize
suits
values
generate_deck
shuffle
end
def generate_deck
#deck = []
#suits.each do |suit|
#values.each { |value| #deck << [suit, value] }
end
end
def suits
#suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades' ]
end
def values
#values = ('2'..'10').to_a + ['J','Q','K','A']
end
#just in case you want to shuffle your deck
def shuffle
#deck.shuffle!
end
end
require 'pp' #this makes the output prettier
new_deck = Deck.new
pp new_deck.deck #calls your current deck so you can see it
Output example:
$ ruby yourfilename.rb
[["Hearts", "K"],
["Spades", "5"],
["Clubs", "7"],
#code omitted... (the rest of your cards would be here)
["Clubs", "K"],
["Hearts", "5"],
["Diamonds", "J"],
["Hearts", "7"]]

Related

Implement and test Card and Deck classes in Ruby

I am having a little trouble getting my code to run and have been at it for at least 4 hours...I can't seem to figure it out. BTW I am a newb at programming.
This is the UML diagram for the card/deck
http://imgur.com/lBJw2z0
class Card
#Cards rank from lowest to highest
VALUE = %w(2 3 4 5 6 7 8 9 10 J Q K A)
SUITS = %w(C D H S)
#(Club Diamond Heart Spade)
def initialize(the_rank, the_suit)
[#rank = the_rank]
[#suit = the_suit]
[#symbols = [nil, nil, '2', '3', '4', '5', '6', '7',
'8', '9', '10', 'J', 'Q', 'K', 'A']
end
def rank
[return #symbols[#rank]]
end
def suit
return #suit
end
def to_s
"#{rank()}#{suit()}"
end
end
#double loop for Deck#initialize
#cards = [ ]
for rank in 2..14
for suit in ['C', 'D', 'H', 'S']
# create a new card with the specified rank
# and suit and append it to the array.
end
end
suits.each do |suit|
(ranks.size).times do |i|
#cards.push(Card.new(ranks[i], suit,(i+1)))
end
end
#Remove a card from the top
def deal
[#cards.pop()](1)
end
#Add card to the bottom of the deck
def add_to_bottom(the_card)
#cards.insert(0, the_card)
end
#Add card to the top of the deck
def add_to_top(the_card)
#cards << the_card
end
#Shuffle the Card objects in the deck
def shuffle!
#cards.shuffle!
end
def count()
#cards.count()
end
def empty?
#cards.length == 0
end
def to_s
string = ""
#cards.each do |card|
string += card.to_s + " "
end
end
def cards
#cards
end
end
I haven't tested this very thoroughly, but it basically implements the UML diagram you linked to:
class Deck
def initialize
#ranks = %w(2 3 4 5 6 7 8 9 10 J Q K A)
#suits = %w(Clubs Diamonds Hearts Spades)
#cards = []
#ranks.each do |rank|
#suits.each do |suit|
#cards << Card.new(rank, suit)
end
end
end
def deal
#cards.shift
end
def add_to_bottom(card)
#cards.unshift(card)
end
def add_to_top(card)
#cards << card
end
def count
#cards.count
end
def empty?
#cards.empty?
end
def shuffle!
#cards.shuffle
end
def to_s
result = ''
#cards.each do |card|
result = result + card.to_s + "\n"
end
return result
end
end
class Card
attr_reader :rank, :suit, :color
def initialize(rank, suit)
#rank = rank
#suit = suit
if #rank == 'Clubs' || #rank == 'Spades'
#color = 'black'
else
#color = 'red'
end
end
def to_s
"Card: #{#color} #{#rank} of #{#suit}"
end
end
# Run it, and try some stuff...
my_deck = Deck.new
puts my_deck.to_s
my_deck.deal
my_deck.count
my_deck.shuffle!
puts my_deck.to_s
my_deck.deal
my_deck.count
Now I am trying to figure how the testing method. I have the first working
d = Deck.new
d.cards.each do |card|
puts "#{card.rank} #{card.suit}"
end
And the second method that needs to test:
tests both the Card and Deck classes using the unit test framework with assert_equal methods. Write unit tests for all methods in the Card class, but you only have to test these methods in the Deck class: new, deal, add_to_bottom, add_to_top, count, empty?

Set.include? for custom objects in Ruby

I have a class roughly like this:
class C
attr_accessor :board # board is a multidimensional array (represents a matrix)
def initialize
#board = ... # initialize board
end
def ==(other)
#board == other.board
end
end
Still, when I do:
s = Set.new
s.add(C.new)
s.include?(C.new) # => false
Why?
Set uses eql? and hash, not ==, to test two objects for equality. See, for example, this documentation of Set: "The equality of each couple of elements is determined according to Object#eql? and Object#hash, since Set uses Hash as storage."
If you want two different C objects to be the same for set membership, you'll have to override those two methods.
class C
attr_accessor :board
def initialize
#board = 12
end
def eql?(other)
#board == other.board
end
def hash
#board.hash
end
end
s = Set.new
s.add C.new
s.include? C.new # => true
You need to do something below :
require 'set'
class C
attr_accessor :board
def initialize
#board = 12
end
def ==(other)
#board == other.board
end
end
s = Set.new
c = C.new
s.add(c)
s.include? c # => true
The reason below will not work:
s.add(C.new)
s.include?(C.new) # => false
Using C.new you create 2 different objects. If you do run C.new thrice then you will get 3 different objects:
C.new.object_id # => 74070710
C.new.object_id # => 74070360
C.new.object_id # => 74070030
Summary : The instance of C you added to s,using Set#add and the instance of C you are checking using Set#include? are 2 different objects. So the result you got is more obvious.
class C
attr_accessor :board # board is a multidimensional array (represents a matrix)
def initialize
board = [[1],[2]] # initialize board
p #board #=> nil !!
end
def ==(other)
#board == other.board
end
def eql?(other) # not used
puts "eql? called"
#board == other.board
end
def ==(other) # not used
puts "== called"
#board == other.board
end
def hash
puts "hash called"
board.hash
end
end
require 'set'
s = Set.new
s.add(c = C.new)
p s.include?(c)
Set uses a Hash as storage underneath. Output:
nil
hash called
hash called
true

Calling a method from a method in another class

It's a fairly well know one. Determining the rank of a poker hand. I created the following classes: Card:
class Card
attr_accessor :suite, :rank, :value
def initialize(suite, rank, value)
#suite = suite
#rank = rank
#value = value
end
def to_s
puts "#{#value}, #{#suite}, #{#rank}"
end
end
Deck:
class Deck
def initialize()
#cardsInDeck = 52
#deck = Array.new()
end
def add_card(card)
#deck.push(card)
end
def deck_size
#deck.length
end
def to_s
#deck.each do |card|
"#{card.rank}, #{card.suite}"
end
end
def shuffle_cards
#deck.shuffle!
end
def deal_cards
#Here I create a new hand object, and when popping cards from the deck
# stack I insert the card into the hand. However, when I want to print
# the cards added to the hand I get the following error:
# : undefined method `each' for #<Hand:0x007fa51c02fd50> (NoMethodError)from
# driver.rb:36:in `<main>'
#hand = Hand.new
for i in 0..51 do
card = #deck.pop
#cardsInDeck -= 1
puts "#{card.value}, #{card.rank}, #{card.suite}"
#hand.add_cards(card)
end
#hand.each do |index|
"#{index.value}, #{index.rank}, #{index.suite}"
end
puts "Cards In Deck: #{#cardsInDeck}"
end
end
Hand
require_relative 'deck'
require_relative 'card'
class Hand
def initialize()
#hand = Array.new()
end
def to_s
count = 0
#hand.each do |card|
"#{card.value}, #{card.rank}, #{card.suite}"
count += 1
end
end
def add_cards(card)
#hand.push(card)
end
def hand_size()
#hand.length
end
end
and Driver File:
require 'logger'
require_relative 'card'
require_relative 'deck'
require_relative 'hand'
suite = ["Hearts", "Diamonds", "Clubs", "Spades"]
rank = ["Ace", 2, 3, 4, 5, 6, 7, 8, 9, 10, "Jack", "Queen", "King"]
deck = Deck.new()
suite.each do |i|
v = 1
rank.each do |j|
deck.add_card(Card.new(i, j, v))
v += 1
end
end
In the Deck class, the deal_card method, I do not understand why looping for an array causes a method error
#hand.each do |index|
"#{index.value}, #{index.rank}, #{index.suite}"
end
puts "Cards In Deck: #{#cardsInDeck}"
#hand is an instance of Hand, and there is no instance method each defined for Hand, so that is why #hand.each is generating an undefined method error.
My answer is not very direct on the error but could hopefully help you in this case.
You method deal_cards is where Dependency Injection can play its role. By the original design Deck has a hard dependency on Hand which is not good and harder to test. You need to change it like
def deal_cards(hand=nil)
#hand = hand || Hand.new
# Others
end
By this you can accept instance outside of Hand, say Foot as long as somebody can play cards by feet!
You can also unit testing this method without writing Hand class at all.
Better to unit test the class instead of manual checking, then you can inject any instance you like into this method during testing.

Ruby hash - how to use hash value when populating object?

For a pack of playing cards:
How can I use the suit hash (below) when creating a pack?
I have:
class PackOfCards
SUITS={H: 'Hearts', S:'Spades', D:'Diamonds', C:'Clubs'}
CARDS=['A','2','3','4','5','6','7','8','9','10','J','Q','K']
attr_accessor :pack_name, :cards
def initialize(pack_name)
#pack_name= pack_name
#cards = []
(0..3).each do |suit|
(0..12).each do |number|
#cards << PlayingCard.new(self, (SUITS[suit].value), CARDS[number])
end
end
end
end
class PlayingCard
attr_accessor :pack, :card_number, :card_suit
def initialize(pack, suit, number)
#card_suit = suit
#card_number = number
end
end
but I get:
pack_of_cards.rb:16:in `block (2 levels) in initialize':
undefined method `value' for
{:H=>"Hearts", :S=>"Spades", :D=>"Diamonds", :C=>"Clubs"}:Hash (NoMethodError)
Your SUITS is invalid expression. Perhaps you wanted to do this:
SUITS = %w[Hearts Spades Diamonds Clubs]
And it is not clear what you are doing, but perhaps you should be doing this:
#cards =
SUITS.flat_map{|suit| CARDS.map{|number| PlayingCard.new(self, suit, number)}}
Here is a corrected version, check the comments :
class PackOfCards
SUITS={H: 'Hearts', S:'Spades', D:'Diamonds', C:'Clubs'} # Use curly braces to define a hash, [] braces will define an array containing one hash
CARDS=['A','2','3','4','5','6','7','8','9','10','J','Q','K']
attr_accessor :pack_name, :cards
def initialize(pack_name)
#pack_name= pack_name
#cards = []
SUITS.each_key do |suit| # each_key is better since it gives you the key of the hash
(0..12).each do |number|
puts PackOfCards::SUITS[suit]
#cards << PlayingCard.new(self, (PackOfCards::SUITS[suit]), PackOfCards::CARDS[number]) # Call the hash with the right key to get the Suit
end
end
end
end
class PlayingCard
attr_accessor :pack, :card_number, :card_suit
def initialize(pack, suit, number)
#card_suit = suit
#card_number = number
end
end
Your Suit definition and lookup don't look valid.
How about something like this (assuming the output is a pack of cards with all suit and numbers) -
class PackOfCards
SUITS = ['Hearts', 'Spades', 'Diamonds', 'Clubs']
CARDS=['A','2','3','4','5','6','7','8','9','10','J','Q','K']
attr_accessor :pack_name, :cards
def initialize(pack_name)
#pack_name= pack_name
#cards = []
(0..3).each do |suit|
(0..12).each do |number|
#cards << PlayingCard.new(self, (SUITS[suit]), CARDS[number])
end
end
end
end
class PlayingCard
attr_accessor :pack, :card_number, :card_suit
def initialize(pack, suit, number)
#card_suit = suit
#card_number = number
end
end
You've actually put a hash in an array. To access the key, value pairs you'd have to access the array element first like this:
SUITS.first[:H]

can instances of a super class communicate with each other just as instances of a normal class

Instances of a class can access methods of other instances, but is it possible to do this for two distinct subclasses of a class with different methods? Or must the methods accessed be included in the super class?
It sounds like you need to keep track of your associations by passing in the Deck instance to the individual Hand instances. Here is an example based on the scenario you described:
class Card
attr_accessor :suit, :value
def initialize(suit, value)
#suit = suit
#value = value
end
end
class Deck
SUITS = ["Spades", "Clubs", "Diamonds", "Hearts"]
VALUES = [2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K", "A"]
HAND_SIZE = 5
attr_accessor :cards
def initialize
shuffle
end
def shuffle
#cards = []
SUITS.each do |suit|
VALUES.each do |value|
#cards << Card.new(suit, value)
end
end
#cards.shuffle!
end
def deal_hand
shuffle if #cards.size < (HAND_SIZE + 1)
hand = #cards[0...HAND_SIZE]
#cards = #cards.drop(HAND_SIZE)
hand
end
end
class Player
attr_accessor :deck
attr_accessor :hand
def initialize(deck)
#deck = deck
end
def draw!
#hand = #deck.deal_hand
end
end
# initialize
the_deck = Deck.new
players = []
players << (player1 = Player.new(the_deck))
players << (player2 = Player.new(the_deck))
players << (player3 = Player.new(the_deck))
players << (player4 = Player.new(the_deck))
# draw
players.each do |player|
player.draw!
end
# play
# ...
Hope this helps.

Resources