Guest User

Untitled

a guest
May 23rd, 2018
230
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.13 KB | None | 0 0
  1. # Blackjack
  2. # Author: Joey Robert <joey@joeyrobert.org>
  3.  
  4. module Blackjack
  5. class Player
  6. attr_accessor :name, :money, :cards, :bets, :pushed
  7.  
  8. def initialize(name)
  9. @name = name
  10. @money = 1000
  11. @cards = [] # usually only 0 is used. higher is used when split
  12. @bets = []
  13. @pushed = 0
  14. end
  15.  
  16. # Two special cases: JQK = 10, A = 1 || 11
  17. def hand_value(deck)
  18. aces = 0
  19.  
  20. value = cards[deck].inject(0) do |memo, card|
  21. if (value = card[:value].to_i) != 0
  22. memo + value
  23. elsif card[:value] == 'A'
  24. aces += 1
  25. memo + 1
  26. else # J, Q, K
  27. memo + 10
  28. end
  29. end
  30.  
  31. # Each ace will add at least one. Add remaining
  32. # part to find value closest to 21
  33. aces.times { value += 10 if value + 10 <= 21 }
  34. value
  35. end
  36.  
  37. def card_display(deck)
  38. buffer = ('+----+ ' * cards[deck].length) + "\n"
  39. cards[deck].each { |card| buffer += "|#{'%-3s' % card[:value]}#{card[:suit]}| " }
  40. buffer += "\n"
  41. buffer += (('| | ' * cards[deck].length) + "\n") * 2
  42. buffer += ('+----+ ' * cards[deck].length) + "\n\n"
  43. buffer
  44. end
  45. end
  46.  
  47. class Dealer < Player
  48. def initialize(name)
  49. super
  50. @pushed = @money = @bets = nil
  51. end
  52.  
  53. def hidden_display
  54. # show only first card
  55. buffer = ('+----+ ' * 2) + "\n"
  56. buffer += "|#{'%-3s' % cards[0][0][:value]}#{cards[0][0][:suit]}| |FACE|\n"
  57. buffer += "| | |DOWN|\n"
  58. buffer += '| | ' * 2 + "\n"
  59. buffer += '+----+ ' * 2 + "\n\n"
  60. buffer
  61. end
  62. end
  63.  
  64. class Deck
  65. SUITS = %w(D H C S) # diamonds, hearts, clubs, spades
  66. # SUITS = %w(♠ ♥ ♦ ♣) might look better if you have a unicode
  67. # terminal, like Gnome terminal
  68. CARDS = %w(2 3 4 5 6 7 8 9 10 J Q K A)
  69.  
  70. def initialize
  71. srand(Time.now.to_i)
  72. reset
  73. shuffle
  74. end
  75.  
  76. def reset
  77. @cards = []
  78.  
  79. SUITS.each do |suit|
  80. CARDS.each do |card|
  81. @cards << {:suit => suit, :value => card}
  82. end
  83. end
  84. end
  85.  
  86. def shuffle
  87. @cards = @cards.sort_by { rand }
  88. end
  89.  
  90. def pop
  91. @cards.pop
  92. end
  93. end
  94.  
  95. class Game
  96. DOLLARS = '$%.2f'
  97.  
  98. def initialize(players)
  99. @players = players
  100. @dealer = Dealer.new('Dealer')
  101. end
  102.  
  103. def run
  104. until @players.empty?
  105. @deck = Deck.new
  106. get_bets
  107. deal
  108. take_turns
  109. results
  110. end
  111. end
  112.  
  113. private
  114.  
  115. def deal
  116. puts 'Dealing cards'
  117. puts '-------------'
  118.  
  119. @players.each do |player|
  120. player.cards = []
  121. player.cards[0] = [@deck.pop, @deck.pop]
  122. end
  123.  
  124. @dealer.cards[0] = [@deck.pop, @deck.pop]
  125. puts 'Dealer\'s hand'
  126. puts @dealer.hidden_display
  127. end
  128.  
  129. def get_bets
  130. puts 'Taking bets'
  131. puts '-----------'
  132.  
  133. @players.reject! { |player| player.money <= 0 && player.pushed <= 0 }
  134. puts "Players still in the game: #{@players.map {|player| "#{player.name} (#{DOLLARS % player.money})"}.join(', ')}\n\n"
  135.  
  136. @players.each do |player|
  137. player.bets = []
  138.  
  139. puts "#{player.name}, you have #{DOLLARS % player.money}."
  140. puts "#{DOLLARS % player.pushed} has been pushed. Your next bet will be in addition to this amount." if player.pushed > 0
  141.  
  142. begin
  143. puts "How much do you want to bet? Your bet must be a multiple of $1, ranging from $1 to #{DOLLARS % player.money}."
  144. player.bets[0] = gets.chomp.to_i
  145. puts 'Invalid bet.' if failing = (player.bets[0] <= 0 || player.bets[0] > player.money)
  146. end while failing
  147.  
  148. player.money -= player.bets[0]
  149. player.bets[0] += player.pushed
  150. player.pushed = 0
  151. puts "#{player.name} bets #{DOLLARS % player.bets[0]}\n\n"
  152. end
  153. end
  154.  
  155. def turn_display(player, deck)
  156. puts "Hand Value: #{player.hand_value(deck)}"
  157. puts "Total bet: #{DOLLARS % player.bets[deck]}" if player.bets
  158. puts player.card_display(deck)
  159. puts "#{player.name} busted.\n\n" if player.hand_value(deck) > 21
  160. puts "#{player.name} has 21!\n\n" if player.hand_value(deck) == 21
  161. end
  162.  
  163. def turn(player, deck)
  164. turn_display(player, deck)
  165.  
  166. opts = ['S']
  167. if player.hand_value(deck) < 21
  168. opts << 'H'
  169. opts << 'D' if player.money >= player.bets[deck] # double down allowed on any two cards, if you have enough money
  170. opts << 'T' if player.cards[deck].length == 2 && player.cards[deck][0][:value] == player.cards[deck][1][:value] # splitting on identical cards
  171. elsif player.hand_value(deck) >= 21 # turn over
  172. return
  173. end
  174.  
  175. puts opts_text(opts)
  176. choice = gets.chomp[0,1].upcase
  177.  
  178. until opts.include?(choice)
  179. puts "That is an invalid choice. #{opts_text(opts)}"
  180. choice = gets.chomp[0,1].upcase
  181. end
  182. puts ''
  183.  
  184. case choice
  185. when 'H'
  186. player.cards[deck] << @deck.pop
  187. turn(player, deck)
  188. when 'D'
  189. player.cards[deck] << @deck.pop
  190. player.money -= player.bets[deck]
  191. player.bets[deck] *= 2
  192. turn_display(player, deck)
  193. when 'T' # should allow arbitrary splitting
  194. player.cards << [player.cards[deck].delete_at(0)]
  195. player.bets << player.bets[deck] / 2.0
  196. player.bets[deck] /= 2.0
  197. turn(player, deck)
  198. when 'S'
  199. return
  200. end
  201. end
  202.  
  203. def take_turns
  204. @players.each do |player|
  205. puts "#{player.name}'s Turn"
  206. player.cards.each_index do |deck|
  207. turn(player, deck)
  208. end
  209. end
  210. end
  211.  
  212. OPTIONS = {'D' => 'Double down', 'S' => 'Stand', 'H' => 'Hit', 'T' => 'Split'}
  213.  
  214. def opts_text(opts)
  215. opts.map { |v| OPTIONS[v] + " (#{v})" }.join(', ') + '?'
  216. end
  217.  
  218. def results
  219. puts "Dealer's Turn"
  220. puts '-------------'
  221. turn_display(@dealer, 0)
  222.  
  223. while(@dealer.hand_value(0) < 17)
  224. @dealer.cards[0] << @deck.pop
  225. turn_display(@dealer, 0)
  226. end
  227.  
  228. number_to_beat = @dealer.hand_value(0)
  229.  
  230. @players.each do |player|
  231. player.cards.each_with_index do |cards, i|
  232. next if cards.nil?
  233. hand_value = player.hand_value(i)
  234.  
  235. if (number_to_beat > 21 || hand_value > number_to_beat) && hand_value <= 21
  236. print "#{player.name} has won #{DOLLARS % player.bets[i]}"
  237. player.money += 2 * player.bets[i]
  238. elsif hand_value == number_to_beat && hand_value <= 21
  239. print "#{player.name} has pushed #{DOLLARS % player.bets[i]}"
  240. player.pushed += player.bets[i]
  241. else
  242. print "#{player.name} has lost #{DOLLARS % player.bets[i]}"
  243. end
  244. puts " (#{player.name}'s #{hand_value} vs. Dealer's #{number_to_beat})"
  245. end
  246. end
  247.  
  248. puts ''
  249. end
  250. end
  251. end
  252.  
  253. # I/O
  254. puts 'Blackjack, by Joey Robert'
  255. puts "-------------------------\n\n"
  256.  
  257. puts 'How many players are playing?'
  258. number_of_players = gets.chomp.to_i
  259. puts ''
  260.  
  261. if number_of_players == 0
  262. puts 'Not enough players. Come back some other time.'
  263. exit(0)
  264. end
  265.  
  266. players = Array.new(number_of_players) do |i|
  267. puts "What's Player #{i + 1}'s name?"
  268. name = gets.chomp
  269. puts ''
  270. Blackjack::Player.new(name)
  271. end
  272.  
  273. Blackjack::Game.new(players).run
Add Comment
Please, Sign In to add comment