diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..18fd4cf --- /dev/null +++ b/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true +# A sample Gemfile +source "https://rubygems.org" + +gem 'sinatra' + +group :development do + gem 'pry-byebug' +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..e53c1fb --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,32 @@ +GEM + remote: https://rubygems.org/ + specs: + byebug (9.0.5) + coderay (1.1.1) + method_source (0.8.2) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-byebug (3.4.0) + byebug (~> 9.0) + pry (~> 0.10) + rack (1.6.4) + rack-protection (1.5.3) + rack + sinatra (1.4.7) + rack (~> 1.5) + rack-protection (~> 1.4) + tilt (>= 1.3, < 3) + slop (3.6.0) + tilt (2.0.5) + +PLATFORMS + ruby + +DEPENDENCIES + pry-byebug + sinatra + +BUNDLED WITH + 1.12.5 diff --git a/app.rb b/app.rb new file mode 100644 index 0000000..1977d11 --- /dev/null +++ b/app.rb @@ -0,0 +1,57 @@ +require 'sinatra' +require 'pry-byebug' +require 'sinatra/reloader' if development? +require './helpers/blackjack_helper.rb' + +enable :sessions + +helpers BlackjackHelper + +get '/' do + session.clear + session['purse'] ||= 1000 + erb :welcome +end + +get '/blackjack' do + deck = session['deck'] = new_deck.shuffle + player_cards = session['player_cards'] = deal(deck, 2) + dealer_cards = session['dealer_cards'] = deal(deck, 2) + + erb :blackjack, locals: { player_cards: player_cards, + dealer_cards: dealer_cards.first, wording: nil, + bet: session['bet'], purse: session['purse']} +end + +post '/blackjack/hit' do + session['player_cards'] << deal(session['deck'], 1).flatten + player_cards = session['player_cards'] + if bust?(player_cards) + redirect('/blackjack/stay') + else + erb :blackjack, locals: {player_cards: session['player_cards'], + dealer_cards: session['dealer_cards'].first, wording: nil } + end +end + + + +# welcome -> form :player_name, creates a game instance, assigns the player name +get '/blackjack/stay' do + dealer_cards = session['dealer_cards'] + while calc_value(dealer_cards) < 17 + dealer_cards << deal( session['deck'], 1 ) + end + wording = final_result(dealer_cards, session['player_cards']) + erb :blackjack, locals: {player_cards: session['player_cards'], + dealer_cards: session['dealer_cards'], wording: wording } + +end + +get '/bet' do + erb :bet +end + +post '/bet' do + handle_bet(params[:bet], session['purse']) +end diff --git a/helpers/README.md b/helpers/README.md new file mode 100644 index 0000000..90bc16d --- /dev/null +++ b/helpers/README.md @@ -0,0 +1,2 @@ +# Morgan Martin's project_cli_blackjack +Hit me! diff --git a/helpers/blackjack_helper.rb b/helpers/blackjack_helper.rb new file mode 100644 index 0000000..5473460 --- /dev/null +++ b/helpers/blackjack_helper.rb @@ -0,0 +1,99 @@ +module BlackjackHelper + def deal(deck, num) + deck.pop(num) + end + + def handle_bet(bet, purse) + bet = bet.to_i + if bet < purse + session['bet'] = bet + session['purse'] -= bet + redirect('/blackjack') + else + get :bet, locals: { message: "Please enter valid bet" } + end + end + + def state(cards) + if calc_value(cards) > 21 + :busted + elsif calc_value(cards) == 21 && cards.length == 2 + :blackjack + else + calc_value(cards) + end + end + + def final_result(dealer_cards, player_cards) + dealer_state = state(dealer_cards) + player_state = state(player_cards) + if dealer_state == player_state + "It was a tie!!" + elsif player_state == :busted + "You lost!" + elsif dealer_state == :blackjack + "The dealer had blackjack! You lose..." + elsif player_state == :blackjack + "You had blackjack, you win!" + elsif player_state < dealer_state + "You lose..." + elsif dealer_state < player_state + "You win!" + end + end + + def cards_total(cards) + cards.map do |card| + if card[0].is_a?(Integer) + card[0] + elsif card[0] == :ace + 11 + else + 10 + end + end.reduce(:+) + + end + + def correct_for_aces(cards, total) + num_of_aces(cards).times do + total -=10 if total > 21 + end + total + end + + def calc_value(cards) + total = cards_total(cards) + correct_for_aces(cards, total) + end + + def bust?(cards) + total_value = calc_value(cards) + if total_value > 21 + true + elsif total_value <= 21 + false + end + end + + # def no_aces?(cards) + # !cards.map { |card| card[0] }.any? { |card| card == :ace } + # end + + def num_of_aces(cards) + cards.select { |card| card[0] == :ace }.length + end + + def new_deck + return_array = [] + ranks.product(suits) + end + + def ranks + (2..10).to_a << :ace << :jack << :queen << :king + end + + def suits + [:spade, :heart, :diamond, :club] + end +end diff --git a/helpers/lib/.ace.rb.swp b/helpers/lib/.ace.rb.swp new file mode 100644 index 0000000..6806a81 Binary files /dev/null and b/helpers/lib/.ace.rb.swp differ diff --git a/helpers/lib/ace.rb b/helpers/lib/ace.rb new file mode 100644 index 0000000..46aaa07 --- /dev/null +++ b/helpers/lib/ace.rb @@ -0,0 +1,20 @@ +class Ace < Card + attr_reader :value + + def initialize(suit, rank, face_up = true) + super + @value = 11 + end + + def toggle_value + @value = @value == 1 ? 11 : 1 + end + + def untoggled? + @value == 11 + end + + def convert_rank_to_value + @value + end +end diff --git a/helpers/lib/bet.rb b/helpers/lib/bet.rb new file mode 100644 index 0000000..ed27f6d --- /dev/null +++ b/helpers/lib/bet.rb @@ -0,0 +1,2 @@ +class Bet +end diff --git a/helpers/lib/blackjack.rb b/helpers/lib/blackjack.rb new file mode 100644 index 0000000..180226f --- /dev/null +++ b/helpers/lib/blackjack.rb @@ -0,0 +1,12 @@ +require './card' +require './ace' +require './hand' +require './view' +require './game_view' +require './player_view' +require './player.rb' +require './human_player' +require './dealer' +require './deck_empty_error' +require './deck' +require './game' diff --git a/helpers/lib/card.rb b/helpers/lib/card.rb new file mode 100644 index 0000000..3ef750e --- /dev/null +++ b/helpers/lib/card.rb @@ -0,0 +1,64 @@ +class Card + attr_reader :suit, :rank + def initialize(suit, rank, face_up = true) + @suit = suit + @rank = rank + @face_up = face_up + end + + def value + convert_rank_to_value + end + + def face_up? + @face_up + end + + def face_down? + !@face_up + end + + def flip + @face_up ^= true + end + + def rank_symbol + convert_rank_to_symbol + end + + def suit_symbol + convert_suit_to_symbol + end + + private + + def convert_rank_to_value + if @rank.is_a?(Symbol) + return 10 if [:jack, :queen, :king].include?(@rank) + else + @rank + end + end + + # Converts rank to a string for rendering + def convert_rank_to_symbol + @rank.is_a?(Symbol) ? @rank.to_s.chars.first.upcase : @rank.to_s + end + + def convert_suit_to_symbol + case @suit + when :diamond + encode("\u2666") + when :heart + encode("\u2665") + when :club + encode("\u2663") + when :spade + encode("\u2660") + end + end + + def encode(string) + string.encode('utf-8') + end +end diff --git a/helpers/lib/dealer.rb b/helpers/lib/dealer.rb new file mode 100644 index 0000000..7c614dd --- /dev/null +++ b/helpers/lib/dealer.rb @@ -0,0 +1,17 @@ +class Dealer < Player + def make_last_card_facedown + @hand.flip_last_card unless last_card_in_hand.face_down? + end + + def show_hidden_card + @hand.flip_last_card unless last_card_in_hand.face_up? + end + + def last_card_in_hand + @hand.last_card + end + + def hit? + @hand.total_value < 17 + end +end diff --git a/helpers/lib/deck.rb b/helpers/lib/deck.rb new file mode 100644 index 0000000..4a7ec8e --- /dev/null +++ b/helpers/lib/deck.rb @@ -0,0 +1,49 @@ +class Deck + def initialize + @cards = new_deck.shuffle + end + + def shuffle! + @cards.shuffle! + end + + def size + @cards.size + end + + def add_card(card) + @cards << card + end + + def get_card + empty? ? (raise DeckEmptyError) : @cards.pop + end + + private + + def empty? + @cards.empty? + end + + def ranks + (2..10).to_a << :ace << :jack << :queen << :king + end + + def suits + [:spade, :heart, :diamond, :club] + end + + def new_deck + return_array = [] + ranks.each do |rank| + suits.each do |suit| + if rank == :ace + return_array << Ace.new(suit, rank) + else + return_array << Card.new(suit, rank) + end + end + end + return_array + end +end diff --git a/helpers/lib/deck_empty_error.rb b/helpers/lib/deck_empty_error.rb new file mode 100644 index 0000000..947c428 --- /dev/null +++ b/helpers/lib/deck_empty_error.rb @@ -0,0 +1,5 @@ +class DeckEmptyError < StandardError + def initialize(msg = "deck empty") + super(msg) + end +end diff --git a/helpers/lib/game.rb b/helpers/lib/game.rb new file mode 100644 index 0000000..ad12902 --- /dev/null +++ b/helpers/lib/game.rb @@ -0,0 +1,146 @@ +class Game + def initialize + @player = HumanPlayer.new + @dealer = Dealer.new + @view = GameView.new + @deck = Deck.new + end + + # Could switch between players using a @current_player... + def play + @player.assign_name + round_loop + print_game_results + end + + private + + def round_loop + until @player.purse_empty? + @player.make_bet + deal + player_turn + dealer_turn + print_round_results + handle_bet_results + reset_hands + end + end + + def handle_bet_results + if @player.blackjack? + @player.handle_blackjack_bet + elsif player_wins_round? + @player.handle_winning_bet + elsif winner == :tie + @player.regain_bet + else + @player.lose_bet + end + end + + # This could be moved to the humanplayer class + def player_turn + hit_loop(@player) + end + + # This could be moved to the dealer class... + def dealer_turn + @dealer.show_hidden_card + render_cards(@dealer.cards_in_hand, "The Dealer shows his hidden card...") + hit_loop(@dealer) unless @player.bust? + end + + def reset_hands + @deck.add_card(@player.remove_card_from_hand) until @player.cards_in_hand.empty? + @deck.add_card(@dealer.remove_card_from_hand) until @dealer.cards_in_hand.empty? + @deck.shuffle! + end + + def print_game_results + @view.print_game_results(@player.purse_amount) + end + + # This could be moved to the gameview class + def print_round_results + @view.print_score_results(@player.hand_value, @dealer.hand_value) + @view.print_blackjack if @player.blackjack? && player_wins_round? + if player_wins_round? + @view.print_player_win + elsif tie? + @view.print_tie + else + @view.print_player_loss + end + end + + def tie? + winner == :tie + end + + def player_wins_round? + winner == @player + end + + def winner + return @dealer if @player.bust? + return @player if @dealer.bust? + return :tie if @dealer.hand_value == @player.hand_value + @dealer.hand_value > @player.hand_value ? @dealer : @player + end + + #This could be moved to the player class + def hit_loop(competitor) + until competitor.bust? || competitor.twenty_one? + sleep 2 + competitor.hit? ? deal_player_cards(competitor, 1) : break + break if competitor.doubled_down? + @view.print_busted if competitor.bust? + @view.print_player_hand(competitor) + end + handle_double_down(competitor) if competitor.doubled_down? + end + + def handle_double_down(competitor) + deal_player_cards(competitor, 1) + @view.print_player_hand(competitor) + sleep 2 + @view.output("About to double bet") + competitor.double_bet + competitor.reset_doubled_down + end + + # This could be moved to the main view class + def render_cards(cards, message = nil) + @view.output(message) if message + @view.render_cards(cards) + end + + # Perhaps move to a deck view? + def deal + @view.print_dealing_message + deal_player_cards(@player, 2) + deal_player_cards(@dealer, 2) + @dealer.make_last_card_facedown + print_dealt_cards + end + + def print_dealt_cards + @view.print_dealt_cards(@player.cards_in_hand, @dealer.cards_in_hand) + end + + def deal_player_cards(player, num_of_cards) + cards = get_cards_from_deck(num_of_cards) + add_cards_to_players_hand(player, cards) + end + + def add_cards_to_players_hand(player, cards) + cards.each { |card| player.add_card_to_hand(card) } + end + + def get_cards_from_deck(num_of_cards) + cards = [] + num_of_cards.times { cards << @deck.get_card } + cards + end +end diff --git a/helpers/lib/game_view.rb b/helpers/lib/game_view.rb new file mode 100644 index 0000000..c812e81 --- /dev/null +++ b/helpers/lib/game_view.rb @@ -0,0 +1,115 @@ +class GameView < View + + def print_resetting_hands + print 'resetting hands' + 3.times do + sleep 1 + print '.' + end + output + end + + def print_dealing_message + print 'dealing' + 3.times do + sleep 1 + print '.' + end + output + end + + def print_game_results(purse_amount) + if purse_amount > 0 + output("You leave the game with #{purse_amount} left over.") + else + output("You went broke...") + end + end + + def print_player_hand(player) + if player.is_a?(HumanPlayer) + output("Your hand:") + render_cards(player.cards_in_hand) + else + output("The Dealer's Cards:") + render_cards(player.cards_in_hand) + end + end + + def print_dealt_cards(player_cards, dealer_cards) + output("Your cards:") + render_cards(player_cards) + output("The Dealer's Cards:") + render_cards(dealer_cards) + output + end + + def print_player_win(name = nil) + if name + output("Congratulations #{name} you win this round!") + else + output("Congrats player you win this round!") + end + end + + def print_blackjack + output("WINNER WINNER CHICKEN DINNER YOU GOT BLACKJACK") + end + + def print_tie + output("It was a draw.") + end + + def print_busted + output("BUSTED") + end + + def print_score_results(player_score, dealer_score) + output("Player's Score: #{player_score}") + output("Dealer's Score: #{dealer_score}") + end + + def print_player_loss + output("The dealer won this round...") + end + + def render_cards(cards) + return_string = '' + cards = cards.map { |card| convert_card_to_render_string(card).split("\n") } + card_render_string_length.times do + cards.each { |card| return_string << (card.shift + " ") } + return_string << "\n" + end + output(return_string) + end + + private + + def card_render_string_length + 7 + end + + def convert_card_to_render_string(card) + if card.face_down? + face_down_card + else + "----------\n" + + "|#{card.rank_symbol}#{' ' * (7 - card.rank_symbol.length)}#{card.suit_symbol}|\n" + + "| |\n" + + "| |\n" + + "| |\n" + + "|#{card.suit_symbol}#{' ' * (7 - card.rank_symbol.length)}#{card.rank_symbol}|\n" + + "----------" + end + end + + def face_down_card + "----------\n" + + "| ** ** |\n" + + "| ** ** |\n" + + "| |\n" + + "|\\ /|\n" + + "| ****** |\n" + + "----------" + end +end diff --git a/helpers/lib/hand.rb b/helpers/lib/hand.rb new file mode 100644 index 0000000..31bf026 --- /dev/null +++ b/helpers/lib/hand.rb @@ -0,0 +1,78 @@ +class Hand + attr_reader :cards + def initialize(cards = []) + @cards = cards + end + + def bust? + if contains_aces? && total_value_over_twenty_one? + toggle_ace if contains_untoggled_aces? + total_value_over_twenty_one? + else + total_value_over_twenty_one? + end + end + + def remove_card + @cards.pop + end + + def total_value + return 0 if @cards.empty? + @cards.map { |card| card.value }.reduce(:+) + end + + def last_card + @cards.last + end + + def flip_last_card + @cards.last.flip + end + + def num_of_cards + @cards.size + end + + def add_card(card) + card.is_a?(Ace) ? add_ace(card) : @cards << card + end + + def blackjack? + num_of_cards == 2 && total_value == 21 + end + + private + + def add_ace(ace) + if total_value + 11 > 21 + ace.toggle_value + @cards << ace + else + @cards << ace + end + end + + def toggle_ace + get_first_untoggled_ace.toggle_value + end + + def get_first_untoggled_ace + @cards.each do |card| + return card if card.is_a?(Ace) && card.untoggled? + end + nil + end + + def contains_untoggled_aces? + @cards.select { |card| card.is_a?(Ace) }.any? { |ace| ace.untoggled? } + end + + def contains_aces? + @cards.any? { |card| card.is_a?(Ace) } + end + + def total_value_over_twenty_one? + total_value > 21 + end +end diff --git a/helpers/lib/human_player.rb b/helpers/lib/human_player.rb new file mode 100644 index 0000000..9375778 --- /dev/null +++ b/helpers/lib/human_player.rb @@ -0,0 +1,80 @@ +class HumanPlayer < Player + def initialize(options = {}) + super + @view = PlayerView.new + @purse = options[:purse] ? options[:purse] : 100 + @bet = 0 + end + + def double_bet + @bet = 2 * @bet + end + + def doubled_down + @doubled_down + end + + def reset_doubled_down + @doubled_down = false + end + + def possible_to_dd + @bet * 2 < @purse + end + + def hit? + result = @view.ask_for_hit_or_dd(possible_to_dd) + case result + when 'y' + true + when 'n' + false + when 'dd' + @doubled_down = true + false + end + end + + def handle_blackjack_bet + @purse += (@bet * (1.5)).round(0) + @bet + @bet = 0 + end + + def handle_winning_bet + @purse += (@bet * 2) + @bet = 0 + end + + def regain_bet + @purse += @bet + @bet = 0 + end + + def lose_bet + @bet = 0 + end + + def blackjack? + @hand.blackjack? + end + + def make_bet + @bet = nil + until !@bet.nil? && @bet <= @purse + @bet = @view.ask_for_bet_amount(purse_amount) + end + @purse -= @bet + end + + def purse_amount + @purse + end + + def purse_empty? + @purse <= 0 + end + + def assign_name + @name = @view.ask_for_name + end +end diff --git a/helpers/lib/player.rb b/helpers/lib/player.rb new file mode 100644 index 0000000..b518fc4 --- /dev/null +++ b/helpers/lib/player.rb @@ -0,0 +1,35 @@ +class Player + attr_reader :name + def initialize(options = {}) + @hand = Hand.new + @doubled_down = false + end + + def doubled_down? + @doubled_down + end + + def bust? + @hand.bust? + end + + def remove_card_from_hand + @hand.remove_card + end + + def add_card_to_hand(card) + @hand.add_card(card) + end + + def twenty_one? + @hand.total_value == 21 + end + + def hand_value + @hand.total_value + end + + def cards_in_hand + @hand.cards + end +end diff --git a/helpers/lib/player_view.rb b/helpers/lib/player_view.rb new file mode 100644 index 0000000..b5dd856 --- /dev/null +++ b/helpers/lib/player_view.rb @@ -0,0 +1,24 @@ +class PlayerView < View + + def ask_for_hit_or_dd(possible_to_dd) + input = nil + until input == 'n' || input == 'y' || + (input == 'dd' && possible_to_dd == true) + output("Can't double down") if input == 'dd' + + output("Would you like to hit (or double down)? dd/y/n") + input = get_input + end + input + end + + def ask_for_name + output("What is your name?") + get_input + end + + def ask_for_bet_amount(purse_amount) + output("How much would you like to bet? Purse Amount: #{purse_amount}") + get_input.to_i + end +end diff --git a/helpers/lib/t.rb b/helpers/lib/t.rb new file mode 100644 index 0000000..2f6537e --- /dev/null +++ b/helpers/lib/t.rb @@ -0,0 +1,5 @@ +require './blackjack' + +g = Game.new + +g.play diff --git a/helpers/lib/view.rb b/helpers/lib/view.rb new file mode 100644 index 0000000..3201cc0 --- /dev/null +++ b/helpers/lib/view.rb @@ -0,0 +1,13 @@ +class View + def get_input(label = nil) + puts label if label + print '> ' + input = gets.chomp + exit if input.start_with?('q') || input == "exit" + input + end + + def output(info = nil) + puts info + end +end diff --git a/helpers/spec/ace_spec.rb b/helpers/spec/ace_spec.rb new file mode 100644 index 0000000..c7c9110 --- /dev/null +++ b/helpers/spec/ace_spec.rb @@ -0,0 +1,20 @@ +require 'card' +require 'ace' + +describe Ace do + + let(:ace_of_spades) { Ace.new(:spade, :ace)} + + describe '#toggle_value' do + it "toggles an ace's value" do + ace_of_spades.toggle_value + expect(ace_of_spades.value).to eq(1) + end + end + + describe '#rank_symbol' do + it 'works for aces' do + expect(ace_of_spades.rank_symbol).to eq('A') + end + end +end diff --git a/helpers/spec/card_spec.rb b/helpers/spec/card_spec.rb new file mode 100644 index 0000000..efa86a7 --- /dev/null +++ b/helpers/spec/card_spec.rb @@ -0,0 +1,48 @@ +require 'card' + +describe Card do + + let(:ten_of_spades) { Card.new(:spade, 10) } + let(:king_of_spades) { Card.new(:spade, :king)} + + describe 'a ten of spades' do + it 'returns its rank' do + expect(ten_of_spades.rank).to eq(10) + end + + it 'returns its suit' do + expect(ten_of_spades.suit).to eq(:spade) + end + + it 'is showing by default' do + expect(ten_of_spades.face_up?).to eq(true) + end + end + + describe '#flip' do + it 'flips the card over' do + ten_of_spades.flip + expect(ten_of_spades.face_up?).to eq(false) + end + end + + describe '#value' do + it "returns a value matching the card's rank if 10 or under" do + expect(ten_of_spades.value).to eq(10) + end + + it 'returns 10 if it is a face card' do + expect(king_of_spades.value).to eq(10) + end + end + + describe '#rank_symbol' do + it 'returns the value if not a face card' do + expect(ten_of_spades.rank_symbol).to eq('10') + end + + it 'returns a capital letter if it is a face card' do + expect(king_of_spades.rank_symbol).to eq('K') + end + end +end diff --git a/helpers/spec/dealer_spec.rb b/helpers/spec/dealer_spec.rb new file mode 100644 index 0000000..afc59ba --- /dev/null +++ b/helpers/spec/dealer_spec.rb @@ -0,0 +1,29 @@ +require 'player' +require 'dealer' +describe Dealer do + + let(:dealer) { Dealer.new } + let(:king_of_spades) { Card.new(:spade, :king) } + let(:seven_of_spades) { Card.new(:spade, 7) } + + describe '#make_last_card_facedown' do + it 'flips the last card' do + dealer.add_card_to_hand(king_of_spades) + dealer.make_last_card_facedown + expect(dealer.last_card_in_hand.face_up?).to eq(false) + end + end + + describe '#hit?' do + it 'returns true if hand value is less than 17' do + dealer.add_card_to_hand(king_of_spades) + expect(dealer.hit?).to eq(true) + end + + it 'returns false if hand value is 17 or greater' do + dealer.add_card_to_hand(king_of_spades) + dealer.add_card_to_hand(seven_of_spades) + expect(dealer.hit?).to eq(false) + end + end +end diff --git a/helpers/spec/deck_spec.rb b/helpers/spec/deck_spec.rb new file mode 100644 index 0000000..ccaf1ee --- /dev/null +++ b/helpers/spec/deck_spec.rb @@ -0,0 +1,34 @@ +require 'deck_empty_error' +require 'deck' + +describe Deck do + + let(:deck) { Deck.new } + let(:card) { Card.new(:spade, :king) } + + describe '#size' do + it 'returns 52' do + expect(deck.size).to eq(52) + end + end + + describe '#get_card' do + it 'returns a card' do + expect(deck.get_card).to be_an_instance_of(Card) + end + + it 'raises an exception if deck is empty' do + 52.times { deck.get_card} + expect { deck.get_card }.to raise_error(DeckEmptyError) + end + end + + describe '#add_card' do + it 'adds a card to the deck' do + deck.add_card(card) + expect(deck.size).to eq(53) + end + end +end + + diff --git a/helpers/spec/hand_spec.rb b/helpers/spec/hand_spec.rb new file mode 100644 index 0000000..e3e17db --- /dev/null +++ b/helpers/spec/hand_spec.rb @@ -0,0 +1,48 @@ +require 'hand' +require 'ace' + +describe Hand do + + let(:hand) { Hand.new } + let(:king_of_spades) { Card.new(:spade, :king) } + let(:ace_of_spades) { Ace.new(:spade, :ace) } + let(:ace_of_hearts) { Ace.new(:heart, :ace) } + + describe '#add_card' do + it 'adds a card' do + hand.add_card(king_of_spades) + expect(hand.num_of_cards).to eq(1) + end + end + + describe '#bust?' do + it 'returns false for a hand totalling less than 21' do + hand.add_card(king_of_spades) + expect(hand.bust?).to eq(false) + end + + it 'returns true for a hand totalling greater than 21' do + 3.times { hand.add_card(king_of_spades) } + expect(hand.bust?).to eq(true) + end + + it 'returns false if aces can be toggled' do + hand.add_card(ace_of_spades) + hand.add_card(ace_of_hearts) + expect(hand.bust?).to eq(false) + end + end + + describe '#total_value' do + + it 'returns 0 if there are no cards' do + expect(hand.total_value).to eq(0) + end + + it 'toggles the min number of aces required to be less than 21' do + hand.add_card(ace_of_spades) + hand.add_card(ace_of_hearts) + expect(hand.total_value).to eq(12) + end + end +end diff --git a/helpers/spec/human_player_spec.rb b/helpers/spec/human_player_spec.rb new file mode 100644 index 0000000..d9fcb7e --- /dev/null +++ b/helpers/spec/human_player_spec.rb @@ -0,0 +1,32 @@ +require 'human_player' +require 'view' +require 'player_view' + +describe HumanPlayer do + + let(:human) { HumanPlayer.new } + let(:poor_human) { HumanPlayer.new( { purse: 0 } )} + + + describe '#purse_amount' do + it 'starts out at 100' do + expect(human.purse_amount).to eq(100) + end + end + + describe '#purse_empty?' do + it 'returns false if purse is not empty' do + expect(human.purse_empty?).to eq(false) + end + + it 'returns true if purse is empty' do + expect(poor_human.purse_empty?).to eq(true) + end + end + + describe '#double_bet' do + it 'doubles winnings' do + #expect(human.) + end + end +end diff --git a/helpers/spec/player_spec.rb b/helpers/spec/player_spec.rb new file mode 100644 index 0000000..10a874e --- /dev/null +++ b/helpers/spec/player_spec.rb @@ -0,0 +1,27 @@ +require 'player' + +describe Player do + + let(:player) { Player.new } + let(:ace_of_spades) { Ace.new(:spade, :ace) } + let(:ten_of_spades) { Card.new(:spade, 10) } + + describe '#twenty_one?' do + it 'returns true if hand values twenty one' do + player.add_card_to_hand(ace_of_spades) + player.add_card_to_hand(ten_of_spades) + expect(player.twenty_one?).to eq(true) + end + + it 'returns false if hand does not value twenty one' do + expect(player.twenty_one?).to eq(false) + end + end + + describe '#hand_value' do + it "returns the value of the player's hand" do + player.add_card_to_hand(ace_of_spades) + expect(player.hand_value).to eq(11) + end + end +end diff --git a/helpers/spec/spec_helper.rb b/helpers/spec/spec_helper.rb new file mode 100644 index 0000000..47b39ce --- /dev/null +++ b/helpers/spec/spec_helper.rb @@ -0,0 +1,103 @@ +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# The `.rspec` file also contains a few flags that are not defaults but that +# users commonly want. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/views/.#welcome.erb b/views/.#welcome.erb new file mode 120000 index 0000000..e849b28 --- /dev/null +++ b/views/.#welcome.erb @@ -0,0 +1 @@ +morgan@mbox.Home.4028:1469188764 \ No newline at end of file diff --git a/views/bet.erb b/views/bet.erb new file mode 100644 index 0000000..997ae44 --- /dev/null +++ b/views/bet.erb @@ -0,0 +1,4 @@ +
diff --git a/views/blackjack.erb b/views/blackjack.erb new file mode 100644 index 0000000..1dd8e15 --- /dev/null +++ b/views/blackjack.erb @@ -0,0 +1,17 @@ +Player hand: <%= player_cards %>
+Dealer hand: <%= dealer_cards %>
+Player bankroll:<%= purse %>
+Bet amounts: <%= bet %>
+ + + + + + +