console-blackjack 1.0.0 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +41 -38
- data/README.md +9 -14
- data/bj.txt +1 -1
- data/console-blackjack.gemspec +2 -2
- data/lib/blackjack.rb +26 -195
- data/lib/blackjack/card.rb +11 -4
- data/lib/blackjack/dealer_hand.rb +11 -9
- data/lib/blackjack/format.rb +8 -0
- data/lib/blackjack/menus.rb +73 -0
- data/lib/blackjack/player_hand.rb +57 -114
- data/lib/blackjack/player_hand_actions.rb +50 -0
- data/lib/blackjack/player_hand_draw.rb +50 -0
- data/lib/blackjack/shoe.rb +24 -26
- data/lib/blackjack/split_hand.rb +40 -0
- data/lib/blackjack/utils.rb +59 -0
- data/spec/factories/card_factory.rb +2 -2
- data/spec/lib/blackjack/card_spec.rb +3 -2
- data/spec/lib/blackjack/format_spec.rb +10 -0
- data/spec/lib/blackjack/hand_spec.rb +2 -2
- data/spec/lib/blackjack/player_hand_spec.rb +29 -30
- data/spec/lib/blackjack/shoe_spec.rb +2 -2
- data/spec/lib/blackjack_spec.rb +0 -7
- metadata +10 -2
data/lib/blackjack/card.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
class Card
|
4
|
-
attr_reader :value, :
|
5
|
+
attr_reader :value, :suit
|
5
6
|
|
6
|
-
def initialize(value,
|
7
|
+
def initialize(value, suit)
|
7
8
|
@value = value
|
8
|
-
@
|
9
|
+
@suit = suit
|
9
10
|
end
|
10
11
|
|
11
12
|
def to_s
|
12
|
-
Card.faces[value][
|
13
|
+
Card.faces[value][suit]
|
13
14
|
end
|
14
15
|
|
15
16
|
def ace?
|
@@ -20,6 +21,12 @@ class Card
|
|
20
21
|
value > 8
|
21
22
|
end
|
22
23
|
|
24
|
+
def self.value(card, count_method, total)
|
25
|
+
value = card.value + 1
|
26
|
+
v = value > 9 ? 10 : value
|
27
|
+
count_method == SOFT && v == 1 && total < 11 ? 11 : v
|
28
|
+
end
|
29
|
+
|
23
30
|
def self.faces
|
24
31
|
[%w[🂡 🂱 🃁 🃑], %w[🂢 🂲 🃂 🃒], %w[🂣 🂳 🃃 🃓], %w[🂤 🂴 🃄 🃔],
|
25
32
|
%w[🂥 🂵 🃅 🃕], %w[🂦 🂶 🃆 🃖], %w[🂧 🂷 🃇 🃗], %w[🂨 🂸 🃈 🃘],
|
@@ -11,15 +11,7 @@ class DealerHand < Hand
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def value(count_method)
|
14
|
-
total =
|
15
|
-
cards.each_with_index do |card, index|
|
16
|
-
next if index == 1 && hide_down_card
|
17
|
-
|
18
|
-
value = card.value + 1
|
19
|
-
v = value > 9 ? 10 : value
|
20
|
-
v = 11 if count_method == SOFT && v == 1 && total < 11
|
21
|
-
total += v
|
22
|
-
end
|
14
|
+
total = total_card_value(count_method)
|
23
15
|
|
24
16
|
if count_method == SOFT && total > 21
|
25
17
|
value(HARD)
|
@@ -28,6 +20,16 @@ class DealerHand < Hand
|
|
28
20
|
end
|
29
21
|
end
|
30
22
|
|
23
|
+
def total_card_value(count_method)
|
24
|
+
total = 0
|
25
|
+
cards.each_with_index do |card, index|
|
26
|
+
next if index == 1 && hide_down_card
|
27
|
+
|
28
|
+
total += Card.value(card, count_method, total)
|
29
|
+
end
|
30
|
+
total
|
31
|
+
end
|
32
|
+
|
31
33
|
def upcard_is_ace?
|
32
34
|
cards.first.ace?
|
33
35
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Menus
|
4
|
+
def draw_game_options
|
5
|
+
puts ' (N) Number of Decks (T) Deck Type (B) Back'
|
6
|
+
loop do
|
7
|
+
c = Blackjack.getc
|
8
|
+
case c
|
9
|
+
when 'n'
|
10
|
+
clear_draw_hands_new_num_decks
|
11
|
+
when 't'
|
12
|
+
clear_draw_hands_new_deck_type
|
13
|
+
clear_draw_hands_bet_options
|
14
|
+
when 'b'
|
15
|
+
clear_draw_hands_bet_options
|
16
|
+
else
|
17
|
+
clear_draw_hands_game_options
|
18
|
+
end
|
19
|
+
break if %w[n t b].include?(c)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_deck_type
|
24
|
+
puts ' (1) Regular (2) Aces (3) Jacks (4) Aces & Jacks (5) Sevens (6) Eights'
|
25
|
+
loop do
|
26
|
+
c = Blackjack.getc.to_i
|
27
|
+
case c
|
28
|
+
when (1..6)
|
29
|
+
shoe.send("new_#{SHOES[c]}")
|
30
|
+
else
|
31
|
+
clear_draw_hands_new_deck_type
|
32
|
+
end
|
33
|
+
break if (1..6).include?(c)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def ask_insurance
|
38
|
+
puts ' Insurance? (Y) Yes (N) No'
|
39
|
+
loop do
|
40
|
+
c = Blackjack.getc
|
41
|
+
case c
|
42
|
+
when 'y'
|
43
|
+
insure_hand
|
44
|
+
when 'n'
|
45
|
+
no_insurance
|
46
|
+
else
|
47
|
+
clear_draw_hands_ask_insurance
|
48
|
+
end
|
49
|
+
break if %w[y n].include?(c)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def draw_bet_options
|
54
|
+
puts ' (D) Deal Hand (B) Change Bet (O) Options (Q) Quit'
|
55
|
+
loop do
|
56
|
+
c = Blackjack.getc
|
57
|
+
case c
|
58
|
+
when 'd'
|
59
|
+
deal_new_hand
|
60
|
+
when 'b'
|
61
|
+
new_bet
|
62
|
+
when 'o'
|
63
|
+
clear_draw_hands_game_options
|
64
|
+
when 'q'
|
65
|
+
clear
|
66
|
+
exit
|
67
|
+
else
|
68
|
+
clear_draw_hands_bet_options
|
69
|
+
end
|
70
|
+
break if %w[d b o].include?(c)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -1,8 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'hand'
|
4
|
+
require_relative 'player_hand_actions'
|
5
|
+
require_relative 'player_hand_draw'
|
4
6
|
|
5
7
|
class PlayerHand < Hand
|
8
|
+
include PlayerHandDraw
|
9
|
+
include PlayerHandActions
|
10
|
+
|
6
11
|
MAX_PLAYER_HANDS = 7
|
7
12
|
|
8
13
|
attr_accessor :blackjack, :bet, :status, :payed, :cards, :stood
|
@@ -21,94 +26,60 @@ class PlayerHand < Hand
|
|
21
26
|
self.payed = true
|
22
27
|
player_hand_value = value(SOFT)
|
23
28
|
|
24
|
-
if dealer_busted
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
elsif player_hand_value < dealer_hand_value
|
29
|
-
blackjack.money -= bet
|
30
|
-
self.status = LOST
|
29
|
+
if player_hand_won?(dealer_busted, dealer_hand_value, player_hand_value)
|
30
|
+
pay_won_hand
|
31
|
+
elsif player_hand_lost?(dealer_hand_value, player_hand_value)
|
32
|
+
collect_lost_hand
|
31
33
|
else
|
32
34
|
self.status = PUSH
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
37
|
-
|
38
|
-
cards.each do |card|
|
39
|
-
value = card.value + 1
|
40
|
-
v = value > 9 ? 10 : value
|
41
|
-
v = 11 if count_method == SOFT && v == 1 && total < 11
|
42
|
-
total += v
|
43
|
-
end
|
44
|
-
|
45
|
-
return value(HARD) if count_method == SOFT && total > 21
|
46
|
-
|
47
|
-
total
|
48
|
-
end
|
49
|
-
|
50
|
-
def done?
|
51
|
-
if played || stood || blackjack? || busted? ||
|
52
|
-
value(SOFT) == 21 ||
|
53
|
-
value(HARD) == 21
|
54
|
-
self.played = true
|
55
|
-
|
56
|
-
if !payed && busted?
|
57
|
-
self.payed = true
|
58
|
-
self.status = LOST
|
59
|
-
blackjack.money -= bet
|
60
|
-
end
|
61
|
-
return true
|
62
|
-
end
|
63
|
-
|
64
|
-
false
|
65
|
-
end
|
66
|
-
|
67
|
-
def can_split?
|
68
|
-
return false if stood || blackjack.player_hands.size >= MAX_PLAYER_HANDS
|
69
|
-
|
70
|
-
return false if blackjack.money < blackjack.all_bets + bet
|
71
|
-
|
72
|
-
cards.size == 2 && cards.first.value == cards.last.value
|
38
|
+
def player_hand_lost?(dealer_hand_value, player_hand_value)
|
39
|
+
player_hand_value < dealer_hand_value
|
73
40
|
end
|
74
41
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
!(stood || cards.size != 2 || blackjack?)
|
42
|
+
def player_hand_won?(dealer_busted, dealer_hand_value, player_hand_value)
|
43
|
+
dealer_busted || player_hand_value > dealer_hand_value
|
79
44
|
end
|
80
45
|
|
81
|
-
def
|
82
|
-
|
46
|
+
def collect_lost_hand
|
47
|
+
blackjack.money -= bet
|
48
|
+
self.status = LOST
|
83
49
|
end
|
84
50
|
|
85
|
-
def
|
86
|
-
|
51
|
+
def pay_won_hand
|
52
|
+
self.bet *= 1.5 if blackjack?
|
53
|
+
blackjack.money += bet
|
54
|
+
self.status = WON
|
87
55
|
end
|
88
56
|
|
89
|
-
def
|
90
|
-
|
57
|
+
def value(count_method)
|
58
|
+
total = cards.inject(0) { |sum, card| sum + Card.value(card, count_method, sum) }
|
91
59
|
|
92
|
-
if
|
93
|
-
|
60
|
+
if count_method == SOFT && total > 21
|
61
|
+
value(HARD)
|
94
62
|
else
|
95
|
-
|
96
|
-
blackjack.current_player_hand.action?
|
63
|
+
total
|
97
64
|
end
|
98
65
|
end
|
99
66
|
|
100
|
-
def
|
101
|
-
|
67
|
+
def done?
|
68
|
+
return false unless no_more_actions?
|
102
69
|
|
103
70
|
self.played = true
|
104
|
-
|
105
|
-
|
71
|
+
collect_busted_hand if !payed && busted?
|
72
|
+
true
|
106
73
|
end
|
107
74
|
|
108
|
-
def
|
109
|
-
self.
|
110
|
-
self.
|
111
|
-
|
75
|
+
def collect_busted_hand
|
76
|
+
self.payed = true
|
77
|
+
self.status = LOST
|
78
|
+
blackjack.money -= bet
|
79
|
+
end
|
80
|
+
|
81
|
+
def no_more_actions?
|
82
|
+
played || stood || blackjack? || busted? || value(SOFT) == 21 || value(HARD) == 21
|
112
83
|
end
|
113
84
|
|
114
85
|
def process
|
@@ -119,66 +90,38 @@ class PlayerHand < Hand
|
|
119
90
|
end
|
120
91
|
end
|
121
92
|
|
122
|
-
def draw(index)
|
123
|
-
out = String.new(' ')
|
124
|
-
cards.each do |card|
|
125
|
-
out << "#{card} "
|
126
|
-
end
|
127
|
-
|
128
|
-
out << ' ⇒ ' << value(SOFT).to_s << ' '
|
129
|
-
|
130
|
-
if status == LOST
|
131
|
-
out << '-'
|
132
|
-
elsif status == WON
|
133
|
-
out << '+'
|
134
|
-
end
|
135
|
-
|
136
|
-
out << '$' << Blackjack.format_money(bet / 100.0)
|
137
|
-
out << ' ⇐' if !played && index == blackjack.current_hand
|
138
|
-
out << ' '
|
139
|
-
|
140
|
-
if status == LOST
|
141
|
-
out << (busted? ? 'Busted!' : 'Lose!')
|
142
|
-
elsif status == WON
|
143
|
-
out << (blackjack? ? 'Blackjack!' : 'Won!')
|
144
|
-
elsif status == PUSH
|
145
|
-
out << 'Push'
|
146
|
-
end
|
147
|
-
|
148
|
-
out << "\n\n"
|
149
|
-
out
|
150
|
-
end
|
151
|
-
|
152
93
|
def action?
|
153
|
-
|
154
|
-
out << '(H) Hit ' if can_hit?
|
155
|
-
out << '(S) Stand ' if can_stand?
|
156
|
-
out << '(P) Split ' if can_split?
|
157
|
-
out << '(D) Double ' if can_dbl?
|
158
|
-
puts out
|
159
|
-
|
94
|
+
draw_actions
|
160
95
|
loop do
|
161
|
-
|
162
|
-
case
|
96
|
+
c = Blackjack.getc
|
97
|
+
case c
|
163
98
|
when 'h'
|
164
|
-
br = true
|
165
99
|
hit
|
166
100
|
when 's'
|
167
|
-
br = true
|
168
101
|
stand
|
169
102
|
when 'p'
|
170
|
-
br = true
|
171
103
|
blackjack.split_current_hand
|
172
104
|
when 'd'
|
173
|
-
br = true
|
174
105
|
dbl
|
175
106
|
else
|
176
|
-
|
177
|
-
blackjack.draw_hands
|
178
|
-
action?
|
107
|
+
clear_draw_hands_action
|
179
108
|
end
|
180
|
-
|
181
|
-
break if br
|
109
|
+
break if %w[h s p d].include?(c)
|
182
110
|
end
|
183
111
|
end
|
112
|
+
|
113
|
+
def clear_draw_hands_action
|
114
|
+
blackjack.clear
|
115
|
+
blackjack.draw_hands
|
116
|
+
action?
|
117
|
+
end
|
118
|
+
|
119
|
+
def draw_actions
|
120
|
+
out = String.new(' ')
|
121
|
+
out << '(H) Hit ' if can_hit?
|
122
|
+
out << '(S) Stand ' if can_stand?
|
123
|
+
out << '(P) Split ' if can_split?
|
124
|
+
out << '(D) Double ' if can_dbl?
|
125
|
+
puts out
|
126
|
+
end
|
184
127
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlayerHandActions
|
4
|
+
def can_split?
|
5
|
+
return false if stood || blackjack.player_hands.size >= PlayerHand::MAX_PLAYER_HANDS
|
6
|
+
|
7
|
+
return false if blackjack.money < blackjack.all_bets + bet
|
8
|
+
|
9
|
+
cards.size == 2 && cards.first.value == cards.last.value
|
10
|
+
end
|
11
|
+
|
12
|
+
def can_dbl?
|
13
|
+
return false if blackjack.money < blackjack.all_bets + bet
|
14
|
+
|
15
|
+
!(stood || cards.size != 2 || blackjack?)
|
16
|
+
end
|
17
|
+
|
18
|
+
def can_stand?
|
19
|
+
!(stood || busted? || blackjack?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def can_hit?
|
23
|
+
!(played || stood || value(HARD) == 21 || blackjack? || busted?)
|
24
|
+
end
|
25
|
+
|
26
|
+
def hit
|
27
|
+
deal_card
|
28
|
+
|
29
|
+
if done?
|
30
|
+
process
|
31
|
+
else
|
32
|
+
blackjack.draw_hands
|
33
|
+
blackjack.current_player_hand.action?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def dbl
|
38
|
+
deal_card
|
39
|
+
|
40
|
+
self.played = true
|
41
|
+
self.bet *= 2
|
42
|
+
process if done?
|
43
|
+
end
|
44
|
+
|
45
|
+
def stand
|
46
|
+
self.stood = true
|
47
|
+
self.played = true
|
48
|
+
process
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlayerHandDraw
|
4
|
+
def draw(index)
|
5
|
+
out = String.new(' ')
|
6
|
+
out << draw_cards
|
7
|
+
out << draw_money(index)
|
8
|
+
out << draw_status
|
9
|
+
out << "\n\n"
|
10
|
+
out
|
11
|
+
end
|
12
|
+
|
13
|
+
def draw_status
|
14
|
+
case status
|
15
|
+
when LOST
|
16
|
+
draw_lost_str
|
17
|
+
when WON
|
18
|
+
draw_won_str
|
19
|
+
when PUSH
|
20
|
+
'Push'
|
21
|
+
else
|
22
|
+
''
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw_lost_str
|
27
|
+
busted? ? 'Busted!' : 'Lose!'
|
28
|
+
end
|
29
|
+
|
30
|
+
def draw_won_str
|
31
|
+
blackjack? ? 'Blackjack!' : 'Won!'
|
32
|
+
end
|
33
|
+
|
34
|
+
def draw_money(index)
|
35
|
+
out = String.new('')
|
36
|
+
out << '-' if status == LOST
|
37
|
+
out << '+' if status == WON
|
38
|
+
out << '$' << Format.money(bet / 100.0)
|
39
|
+
out << ' ⇐' if !played && index == blackjack.current_hand
|
40
|
+
out << ' '
|
41
|
+
out
|
42
|
+
end
|
43
|
+
|
44
|
+
def draw_cards
|
45
|
+
out = String.new('')
|
46
|
+
out << cards.map { |card| "#{card} " }.join
|
47
|
+
out << ' ⇒ ' << value(SOFT).to_s << ' '
|
48
|
+
out
|
49
|
+
end
|
50
|
+
end
|