console-blackjack 1.0.0

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.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Card
4
+ attr_reader :value, :suite
5
+
6
+ def initialize(value, suite)
7
+ @value = value
8
+ @suite = suite
9
+ end
10
+
11
+ def to_s
12
+ Card.faces[value][suite]
13
+ end
14
+
15
+ def ace?
16
+ value.zero?
17
+ end
18
+
19
+ def ten?
20
+ value > 8
21
+ end
22
+
23
+ def self.faces
24
+ [%w[🂡 🂱 🃁 🃑], %w[🂢 🂲 🃂 🃒], %w[🂣 🂳 🃃 🃓], %w[🂤 🂴 🃄 🃔],
25
+ %w[🂥 🂵 🃅 🃕], %w[🂦 🂶 🃆 🃖], %w[🂧 🂷 🃇 🃗], %w[🂨 🂸 🃈 🃘],
26
+ %w[🂩 🂹 🃉 🃙], %w[🂪 🂺 🃊 🃚], %w[🂫 🂻 🃋 🃛], %w[🂭 🂽 🃍 🃝],
27
+ %w[🂮 🂾 🃎 🃞], %w[🂠]]
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'hand'
4
+
5
+ class DealerHand < Hand
6
+ attr_accessor :blackjack, :hide_down_card
7
+
8
+ def initialize(blackjack)
9
+ super(blackjack)
10
+ @hide_down_card = true
11
+ end
12
+
13
+ def value(count_method)
14
+ total = 0
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
23
+
24
+ if count_method == SOFT && total > 21
25
+ value(HARD)
26
+ else
27
+ total
28
+ end
29
+ end
30
+
31
+ def upcard_is_ace?
32
+ cards.first.ace?
33
+ end
34
+
35
+ def draw
36
+ out = String.new(' ')
37
+ cards.each_with_index do |card, index|
38
+ out << (index == 1 && hide_down_card ? Card.faces[13][0] : card).to_s
39
+ out << ' '
40
+ end
41
+ out << ' ⇒ ' << value(SOFT).to_s
42
+ end
43
+
44
+ def deal_required_cards
45
+ soft, hard = both_values
46
+ while soft < 18 && hard < 17
47
+ deal_card
48
+ soft, hard = both_values
49
+ end
50
+ end
51
+
52
+ def both_values
53
+ [value(SOFT), value(HARD)]
54
+ end
55
+
56
+ def play
57
+ playing = blackjack.need_to_play_dealer_hand?
58
+ self.hide_down_card = false if blackjack? || playing
59
+ deal_required_cards if playing
60
+ self.played = true
61
+ blackjack.pay_hands
62
+ end
63
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ UNKNOWN = 0
4
+ WON = 1
5
+ LOST = 2
6
+ PUSH = 3
7
+
8
+ SOFT = 4
9
+ HARD = 5
10
+
11
+ class Hand
12
+ attr_accessor :cards, :blackjack, :played
13
+
14
+ def initialize(blackjack)
15
+ @blackjack = blackjack
16
+ @played = false
17
+ @cards = []
18
+ end
19
+
20
+ def busted?
21
+ value(SOFT) > 21
22
+ end
23
+
24
+ def deal_card
25
+ cards << blackjack.shoe.next_card
26
+ end
27
+
28
+ def blackjack?
29
+ return false if cards.size != 2
30
+
31
+ cards.first.ace? && cards.last.ten? || cards.first.ten? && cards.last.ace?
32
+ end
33
+ end
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'hand'
4
+
5
+ class PlayerHand < Hand
6
+ MAX_PLAYER_HANDS = 7
7
+
8
+ attr_accessor :blackjack, :bet, :status, :payed, :cards, :stood
9
+
10
+ def initialize(blackjack, bet)
11
+ super(blackjack)
12
+ @bet = bet
13
+ @status = UNKNOWN
14
+ @payed = false
15
+ @stood = false
16
+ end
17
+
18
+ def pay(dealer_hand_value, dealer_busted)
19
+ return if payed
20
+
21
+ self.payed = true
22
+ player_hand_value = value(SOFT)
23
+
24
+ if dealer_busted || player_hand_value > dealer_hand_value
25
+ self.bet *= 1.5 if blackjack?
26
+ blackjack.money += bet
27
+ self.status = WON
28
+ elsif player_hand_value < dealer_hand_value
29
+ blackjack.money -= bet
30
+ self.status = LOST
31
+ else
32
+ self.status = PUSH
33
+ end
34
+ end
35
+
36
+ def value(count_method)
37
+ total = 0
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
73
+ end
74
+
75
+ def can_dbl?
76
+ return false if blackjack.money < blackjack.all_bets + bet
77
+
78
+ !(stood || cards.size != 2 || blackjack?)
79
+ end
80
+
81
+ def can_stand?
82
+ !(stood || busted? || blackjack?)
83
+ end
84
+
85
+ def can_hit?
86
+ !(played || stood || value(HARD) == 21 || blackjack? || busted?)
87
+ end
88
+
89
+ def hit
90
+ deal_card
91
+
92
+ if done?
93
+ process
94
+ else
95
+ blackjack.draw_hands
96
+ blackjack.current_player_hand.action?
97
+ end
98
+ end
99
+
100
+ def dbl
101
+ deal_card
102
+
103
+ self.played = true
104
+ self.bet *= 2
105
+ process if done?
106
+ end
107
+
108
+ def stand
109
+ self.stood = true
110
+ self.played = true
111
+ process
112
+ end
113
+
114
+ def process
115
+ if blackjack.more_hands_to_play?
116
+ blackjack.play_more_hands
117
+ else
118
+ blackjack.play_dealer_hand
119
+ end
120
+ end
121
+
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
+ def action?
153
+ out = String.new(' ')
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
+
160
+ loop do
161
+ br = false
162
+ case Blackjack.getc
163
+ when 'h'
164
+ br = true
165
+ hit
166
+ when 's'
167
+ br = true
168
+ stand
169
+ when 'p'
170
+ br = true
171
+ blackjack.split_current_hand
172
+ when 'd'
173
+ br = true
174
+ dbl
175
+ else
176
+ blackjack.clear
177
+ blackjack.draw_hands
178
+ action?
179
+ end
180
+
181
+ break if br
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'card'
4
+
5
+ class Shoe
6
+ attr_accessor :num_decks, :cards
7
+
8
+ def initialize(num_decks = 1)
9
+ @num_decks = num_decks
10
+ @cards = []
11
+ end
12
+
13
+ def needs_to_shuffle?
14
+ return true if cards.size.zero?
15
+
16
+ total_cards = num_decks * 52
17
+ cards_dealt = total_cards - cards.size
18
+ used = cards_dealt / total_cards.to_f * 100.0
19
+
20
+ Shoe.shuffle_specs.each do |spec|
21
+ return true if used > spec.first && num_decks == spec.last
22
+ end
23
+
24
+ false
25
+ end
26
+
27
+ def shuffle
28
+ 7.times { cards.shuffle! }
29
+ end
30
+
31
+ def new_regular
32
+ self.cards = []
33
+ num_decks.times do
34
+ (0..3).each do |suite_value|
35
+ (0..12).each do |value|
36
+ cards << Card.new(value, suite_value)
37
+ end
38
+ end
39
+ end
40
+ shuffle
41
+ end
42
+
43
+ def new_aces
44
+ self.cards = []
45
+ (num_decks * 10).times do
46
+ (0..3).each do |suite_value|
47
+ cards << Card.new(0, suite_value)
48
+ end
49
+ end
50
+ shuffle
51
+ end
52
+
53
+ def new_jacks
54
+ self.cards = []
55
+ (num_decks * 10).times do
56
+ (0..3).each do |suite_value|
57
+ cards << Card.new(10, suite_value)
58
+ end
59
+ end
60
+ shuffle
61
+ end
62
+
63
+ def new_aces_jacks
64
+ self.cards = []
65
+ (num_decks * 10).times do
66
+ (0..3).each do |suite_value|
67
+ cards << Card.new(0, suite_value)
68
+ cards << Card.new(10, suite_value)
69
+ end
70
+ end
71
+ shuffle
72
+ end
73
+
74
+ def new_sevens
75
+ self.cards = []
76
+ (num_decks * 10).times do
77
+ (0..3).each do |suite_value|
78
+ cards << Card.new(6, suite_value)
79
+ end
80
+ end
81
+ shuffle
82
+ end
83
+
84
+ def new_eights
85
+ self.cards = []
86
+ (num_decks * 10).times do
87
+ (0..3).each do |suite_value|
88
+ cards << Card.new(7, suite_value)
89
+ end
90
+ end
91
+ shuffle
92
+ end
93
+
94
+ def next_card
95
+ cards.shift
96
+ end
97
+
98
+ def self.shuffle_specs
99
+ [[95, 8],
100
+ [92, 7],
101
+ [89, 6],
102
+ [86, 5],
103
+ [84, 4],
104
+ [82, 3],
105
+ [81, 2],
106
+ [80, 1]]
107
+ end
108
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :blackjack do
5
+ shoe { nil }
6
+ money { 10_000 }
7
+ player_hands { [] }
8
+ dealer_hand { nil }
9
+ num_decks { 1 }
10
+ current_bet { 500 }
11
+ current_hand { 0 }
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :card do
5
+ value { 0 }
6
+ suite { 0 }
7
+
8
+ trait :ace do
9
+ value { 0 }
10
+ end
11
+
12
+ trait :two do
13
+ value { 1 }
14
+ end
15
+
16
+ trait :five do
17
+ value { 4 }
18
+ end
19
+
20
+ trait :six do
21
+ value { 5 }
22
+ end
23
+
24
+ trait :seven do
25
+ value { 6 }
26
+ end
27
+
28
+ trait :eight do
29
+ value { 7 }
30
+ end
31
+
32
+ trait :nine do
33
+ value { 8 }
34
+ end
35
+
36
+ trait :ten do
37
+ value { 9 }
38
+ end
39
+
40
+ initialize_with { new(value, suite) }
41
+ end
42
+ end