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 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.
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
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.
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.
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.
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
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.