Jim's
Tutorials

Fall 2018
course
site

rails week 8

most complicated object yet

So this curriculum wants me to make a blackjack game and its the most complicated thing I've tried to make so far. It's telling me to make a card class to represent an individual playing card, that contains the suit and the value and provides methods for determining what type of card it is (e.g. face card or ace). I'm supposed to write the code to implement this is a separate file (lib.rb)

I'm supposed to make a Deck class to represent a collection of 52 cards. When dealing a hand this class can be used to supply the Card objects, and put the code that implements that into that same separate file.

Also need to make a Hand class to represent the player's and dealer's hand. This class will need to determine the best score based on the cards that have been dealt.

And that seperate file I keep mentioning is supposed to contain the logic of the game and interact w the classes.

card class


# Card to represent an individual playing card. This class should contain the suit and the value and provide methods for determining what type of card it is (e.g. face card or ace).


class Card
    attr_accessor :suite, :rank
    def initialize(suite, rank)
        @rank = rank
        @suite = suite
    end

    def face_card?
        ['j', 'q', 'k'].include?(@rank)
    end
end

OK so this was pretty simple.

deck class

require_relative 'card'
class Deck
    attr_accessor :deck_o_cards
    def initialize
        suites = ['c','h','s']
        ranks = ['a', 2, 3, 4,'j']
        @deck_o_cards = []
        suites.each do |suite|
            ranks.each do |rank|
                @deck_o_cards << Card.new(suite,rank)
            end
        end
        @deck_o_cards.shuffle!
    end

def deal!(card)
    @dealt_card = @deck_o_cards.pop do |card|
    puts "player was delt #{@dealt_card}card"
    puts "dealer was dealt #{@dealt_card} card"
end
end



end

deck = Deck.new
deck.deal!(1)

now this is getting more complicated

hand class

require_relative 'deck.rb'
require_relative 'card.rb'
require_relative 'hand.rb'


class Hand
    attr_accessor :card_1, :card_2
    def initialize(card_1, card_2)
        @card_1 = card_1
        @card_2 = card_2
    end


    def player_hits
        puts "hit or stand? (choose 'hit' or 'stand')"
        hORs_input = gets.chomp.downcase
        if hORs_input == 'hit'
            puts "another card"
        end
    end
end




hand = Hand.new(3,5)
hand.player_hits()

now things are getting muddled.

overall things confusing me so far

definitely confused about how to call my different classes and how all the different files connect. like, I make a variable with the @ symbol in one file and so is it accessible in another file and how do i conceptualize when and where to call it? so confusing. Also I sense that I'm doing things in my hand and deck objects that i should actually be implementing in my lib.rb file. For instance, i probably shoulden't have any 'puts' statements in those files because they are supposed to only contain the methods/classes. I need to refactor this but its hard to know how to do that if I don't understand the way these methods and objects even connect or cross over to other files. damn.

its a new day

I started again trying to figure out how to connect my instance variables between files. My question was as follows:

# so how do i take the @dealt card from this:
#     def deal(card)
#         @dealt_card = @deck_o_cards.pop do |card|
#         puts "player was delt #{@dealt_card}card"
#         puts "dealer was dealt #{@dealt_card} card"
#     end
#     end
#
#     ...in deck and get it into
#
#     def player_hits
#         puts "hit or stand? (choose 'hit' or 'stand')"
#         hORs_input = gets.chomp.downcase
#         if hORs_input == 'hit'
#             puts "#{@dealt_card} was added to your deck"
#         end
#     end
# end
#
# in hand? it's not showing up when I do it this way, I have a require relative between the files no problem.

and I looked up this resource to help me. I tried various combinations of replacing the (dealt_card) with (deal) and I tried using the 'module' thing they talked about in this article- that if you wrap the class w the variable you want to share in that module thing, you can share the variable across files if you 'include' the classname in the file you're looking to implement the shared variable in. It didin't work, I'm not sure if im putting the module in the wrong place for the variable im looking to share or if the include should be elsewhere in the other file.

Lets try to understand this again...

So, I've been messing with this for a few days and managed to make some progress. I understand how to get variables across classes better, now. I'll show the code at this stage and talk about each file based on my new understanding. my Card.rb still looks like this:

class Card
    attr_accessor :suite, :rank
    def initialize(suite, rank)
        @rank = rank
        @suite = suite
    end

    def face_card?
        ['j', 'q', 'k'].include?(@rank)
    end



end


...it includes a method for checking if my hand includes a face card, which I'll explain as it is used in later files.

Next up is the deck into which I need to put each card.


require_relative 'card'

class Deck
    attr_accessor :deck_o_cards
    def initialize
        suites = ['clubs','hearts','spades', 'diamonds']
        ranks = ['a', 2, 3, 4,5,6,7,8,9,10,'j', 'q', 'k']
        @deck_o_cards = []
        suites.each do |suite|
            ranks.each do |rank|
                @deck_o_cards << Card.new(suite,rank)
            end
        end
        @deck_o_cards.shuffle!
    end

def deal
    @dealt_card = @deck_o_cards.pop
end
end

...So I made the suites and ranks actually accurate. I push them into the deck o cards array which I made an instance variable with the @ symbol. This is because I'm using deck o cards in another method in the same class. I implement the shuffle method to get a random draw on deck o cards and made a method 'deal' that takes the last card and returns it using .pop. I'm calling this the 'dealt_card' and made it a local variable. (I'm trying to think about our last discussion and scope, and how that relates to everything). I read this resources about instance variables to be sure that I'm doing this right.

So, to talk about scope more.

class Deck
    attr_accessor :deck_o_cards
    def initialize
        suites = ['clubs','hearts','spades', 'diamonds']
        ranks = ['a', 2, 3, 4,5,6,7,8,9,10,'j', 'q', 'k']
        @deck_o_cards = []


If i have deck_o_cards as an instance variable I can use it in another method within the same class. putting it in the accessor, means I can use it in another object or method outside of the class. The way I need to do this, though, is pretty particular. I can't just call "Deck.new(deck_o_cards)" or something from another object or method. I'd need to do this:

new_deck = Deck.new
new_deck.deck_o_cards

I'd need to MAKE A NEW DECK first then call deck_o_cards on that NEW DECK... this is revolutionary ... took me so long to understand this. I FINALLY UNDERSTAND WHAT ACCESSORS DO AND HOW TO USE THEM. And it makes sense that you wouldn't need to make something an accessor if you're not intending to call it in another object or method.

lets get back to making this thing now that I understand how to encapsulate my variables. Next comes the hand class, which holds the game logic:

require_relative 'deck'
require_relative 'card'

class Hand
    attr_accessor :card_1, :card_2
    def initialize(card_1, card_2)
        @card_1 = card_1
        @card_2 = card_2
    end

    def display_score
        sum = 0

        if @card_1.face_card? == false
        sum =+ @card_1.rank
    end

        if @card_2.face_card? == false
        sum =+ @card_2.rank
    end
        if @card_1.face_card? == true
            sum += 10
    end
        if @card_2.face_card? == true
            sum += 10
    end
#ace logic
        if @card_1.rank = 'a' || if @card_2.rank = 'a'
            if sum <= 10
                    sum = sum += 11
                else
                    sum = sum += 1

        end
        end
        return sum
        end

    end
end


FINALLY here is the lib.rb file that processes all of these methods and things and makes it all get spit out properly:

require_relative 'deck'
require_relative 'hand'
require_relative 'card'

puts "would you like to play blackjack? (y/n)"
start = gets.chomp
if start == 'y'
    new_deck = Deck.new
    new_deck.deal
    player_hand = Hand.new(new_deck.deal, new_deck.deal)
    puts "you have an #{player_hand.card_1.rank} of #{player_hand.card_1.suite} and a #{player_hand.card_2.rank} of #{player_hand.card_2.suite} in your hand"
    player_score = player_hand.display_score
    puts "#{player_score}"

end

my output looks like this:

ruby lib.rb
would you like to play blackjack? (y/n)
y
you have an j of clubs and a 8 of diamonds in your hand
18

needed to make it so I could add a card to my hand??

I realized I needed to make a method, "add card" so I could add a card to my hand when im prompted to hit or stand. refactored my hand class to look like this (i put the two cards into an array so id be able to hold more in the array)

class Hand
    attr_accessor :card_1, :card_2
    def initialize(card_1, card_2)
        @card_1 = card_1
        @card_2 = card_2
        @hand_array = [@card_1, @card_2]

    end

    def add_card(card)
        @hand_array << card
    end

...and here's my lib.rb file that implements it:

require_relative 'deck'
require_relative 'hand'
require_relative 'card'

puts "would you like to play blackjack? (y/n)"
start = gets.chomp
if start == 'y'
    new_deck = Deck.new
    new_deck.deal
    player_hand = Hand.new(new_deck.deal, new_deck.deal)
    puts "you have an #{player_hand.card_1.rank} of #{player_hand.card_1.suite} and a #{player_hand.card_2.rank} of #{player_hand.card_2.suite} in your hand"
    player_score = player_hand.display_score
    puts "#{player_score}"

    puts 'hit or stand?'
    answer = gets.chomp
    if answer == 'hit'
        new_card = new_deck.deal
        added_hand = player_hand.add_card(new_card)
        puts "you were delt a #{new_card.rank} of #{new_card.suite} "

end
end


OUTPUT:

ruby lib.rb
would you like to play blackjack? (y/n)
y
you have an 8 of clubs and a k of hearts in your hand
18
hit or stand?
hit
you were delt a k of diamonds

...figuring out how to display a single card that had been added to the hand, without, of course, making an entirely new hand, was tricky. so making a new_card variable in my lib.rb file and setting it equal to new_deck.deal let me use the add_card method as long as I attacked it to player_hand (which kept the other cards I'd already drawn) yah and had to pass an argument through it since add_card needed an argument which was the new card.

OMG this is complicated.