Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Blackjack
- # Author: Joey Robert <joey@joeyrobert.org>
- module Blackjack
- class Player
- attr_accessor :name, :money, :cards, :bets, :pushed
- def initialize(name)
- @name = name
- @money = 1000
- @cards = [] # usually only 0 is used. higher is used when split
- @bets = []
- @pushed = 0
- end
- # Two special cases: JQK = 10, A = 1 || 11
- def hand_value(deck)
- aces = 0
- value = cards[deck].inject(0) do |memo, card|
- if (value = card[:value].to_i) != 0
- memo + value
- elsif card[:value] == 'A'
- aces += 1
- memo + 1
- else # J, Q, K
- memo + 10
- end
- end
- # Each ace will add at least one. Add remaining
- # part to find value closest to 21
- aces.times { value += 10 if value + 10 <= 21 }
- value
- end
- def card_display(deck)
- buffer = ('+----+ ' * cards[deck].length) + "\n"
- cards[deck].each { |card| buffer += "|#{'%-3s' % card[:value]}#{card[:suit]}| " }
- buffer += "\n"
- buffer += (('| | ' * cards[deck].length) + "\n") * 2
- buffer += ('+----+ ' * cards[deck].length) + "\n\n"
- buffer
- end
- end
- class Dealer < Player
- def initialize(name)
- super
- @pushed = @money = @bets = nil
- end
- def hidden_display
- # show only first card
- buffer = ('+----+ ' * 2) + "\n"
- buffer += "|#{'%-3s' % cards[0][0][:value]}#{cards[0][0][:suit]}| |FACE|\n"
- buffer += "| | |DOWN|\n"
- buffer += '| | ' * 2 + "\n"
- buffer += '+----+ ' * 2 + "\n\n"
- buffer
- end
- end
- class Deck
- SUITS = %w(D H C S) # diamonds, hearts, clubs, spades
- # SUITS = %w(♠ ♥ ♦ ♣) might look better if you have a unicode
- # terminal, like Gnome terminal
- CARDS = %w(2 3 4 5 6 7 8 9 10 J Q K A)
- def initialize
- srand(Time.now.to_i)
- reset
- shuffle
- end
- def reset
- @cards = []
- SUITS.each do |suit|
- CARDS.each do |card|
- @cards << {:suit => suit, :value => card}
- end
- end
- end
- def shuffle
- @cards = @cards.sort_by { rand }
- end
- def pop
- @cards.pop
- end
- end
- class Game
- DOLLARS = '$%.2f'
- def initialize(players)
- @players = players
- @dealer = Dealer.new('Dealer')
- end
- def run
- until @players.empty?
- @deck = Deck.new
- get_bets
- deal
- take_turns
- results
- end
- end
- private
- def deal
- puts 'Dealing cards'
- puts '-------------'
- @players.each do |player|
- player.cards = []
- player.cards[0] = [@deck.pop, @deck.pop]
- end
- @dealer.cards[0] = [@deck.pop, @deck.pop]
- puts 'Dealer\'s hand'
- puts @dealer.hidden_display
- end
- def get_bets
- puts 'Taking bets'
- puts '-----------'
- @players.reject! { |player| player.money <= 0 && player.pushed <= 0 }
- puts "Players still in the game: #{@players.map {|player| "#{player.name} (#{DOLLARS % player.money})"}.join(', ')}\n\n"
- @players.each do |player|
- player.bets = []
- puts "#{player.name}, you have #{DOLLARS % player.money}."
- puts "#{DOLLARS % player.pushed} has been pushed. Your next bet will be in addition to this amount." if player.pushed > 0
- begin
- puts "How much do you want to bet? Your bet must be a multiple of $1, ranging from $1 to #{DOLLARS % player.money}."
- player.bets[0] = gets.chomp.to_i
- puts 'Invalid bet.' if failing = (player.bets[0] <= 0 || player.bets[0] > player.money)
- end while failing
- player.money -= player.bets[0]
- player.bets[0] += player.pushed
- player.pushed = 0
- puts "#{player.name} bets #{DOLLARS % player.bets[0]}\n\n"
- end
- end
- def turn_display(player, deck)
- puts "Hand Value: #{player.hand_value(deck)}"
- puts "Total bet: #{DOLLARS % player.bets[deck]}" if player.bets
- puts player.card_display(deck)
- puts "#{player.name} busted.\n\n" if player.hand_value(deck) > 21
- puts "#{player.name} has 21!\n\n" if player.hand_value(deck) == 21
- end
- def turn(player, deck)
- turn_display(player, deck)
- opts = ['S']
- if player.hand_value(deck) < 21
- opts << 'H'
- opts << 'D' if player.money >= player.bets[deck] # double down allowed on any two cards, if you have enough money
- opts << 'T' if player.cards[deck].length == 2 && player.cards[deck][0][:value] == player.cards[deck][1][:value] # splitting on identical cards
- elsif player.hand_value(deck) >= 21 # turn over
- return
- end
- puts opts_text(opts)
- choice = gets.chomp[0,1].upcase
- until opts.include?(choice)
- puts "That is an invalid choice. #{opts_text(opts)}"
- choice = gets.chomp[0,1].upcase
- end
- puts ''
- case choice
- when 'H'
- player.cards[deck] << @deck.pop
- turn(player, deck)
- when 'D'
- player.cards[deck] << @deck.pop
- player.money -= player.bets[deck]
- player.bets[deck] *= 2
- turn_display(player, deck)
- when 'T' # should allow arbitrary splitting
- player.cards << [player.cards[deck].delete_at(0)]
- player.bets << player.bets[deck] / 2.0
- player.bets[deck] /= 2.0
- turn(player, deck)
- when 'S'
- return
- end
- end
- def take_turns
- @players.each do |player|
- puts "#{player.name}'s Turn"
- player.cards.each_index do |deck|
- turn(player, deck)
- end
- end
- end
- OPTIONS = {'D' => 'Double down', 'S' => 'Stand', 'H' => 'Hit', 'T' => 'Split'}
- def opts_text(opts)
- opts.map { |v| OPTIONS[v] + " (#{v})" }.join(', ') + '?'
- end
- def results
- puts "Dealer's Turn"
- puts '-------------'
- turn_display(@dealer, 0)
- while(@dealer.hand_value(0) < 17)
- @dealer.cards[0] << @deck.pop
- turn_display(@dealer, 0)
- end
- number_to_beat = @dealer.hand_value(0)
- @players.each do |player|
- player.cards.each_with_index do |cards, i|
- next if cards.nil?
- hand_value = player.hand_value(i)
- if (number_to_beat > 21 || hand_value > number_to_beat) && hand_value <= 21
- print "#{player.name} has won #{DOLLARS % player.bets[i]}"
- player.money += 2 * player.bets[i]
- elsif hand_value == number_to_beat && hand_value <= 21
- print "#{player.name} has pushed #{DOLLARS % player.bets[i]}"
- player.pushed += player.bets[i]
- else
- print "#{player.name} has lost #{DOLLARS % player.bets[i]}"
- end
- puts " (#{player.name}'s #{hand_value} vs. Dealer's #{number_to_beat})"
- end
- end
- puts ''
- end
- end
- end
- # I/O
- puts 'Blackjack, by Joey Robert'
- puts "-------------------------\n\n"
- puts 'How many players are playing?'
- number_of_players = gets.chomp.to_i
- puts ''
- if number_of_players == 0
- puts 'Not enough players. Come back some other time.'
- exit(0)
- end
- players = Array.new(number_of_players) do |i|
- puts "What's Player #{i + 1}'s name?"
- name = gets.chomp
- puts ''
- Blackjack::Player.new(name)
- end
- Blackjack::Game.new(players).run
Add Comment
Please, Sign In to add comment