-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbj_components.py
371 lines (291 loc) · 11.6 KB
/
bj_components.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# -*- coding: utf-8 -*-
"""
Created on Wed Jul 24 16:09:45 2024
Author: Alexandros Stratoudakis
e-mail: [email protected]
Module of classes that are essential to make the game.
TODO: Make bot player class
"""
import random
import sys
from utils import bj_vals, count_value, actions, is_numerical, suits_map, vals_map
from utils import colors
bg = colors.bg
fg = colors.fg
class card():
""" A class that represents a playing card."""
def __init__(self, suit: str, value: str | int):
"""
Initializer function of the card object.
Args:
suit (str): Card's suit ( one of '♥', '♦', '♠', '♣' or 'heart', 'diamond', etc)
value (str | int): Card's value. ( can be 'A', '2', '3', '4', '5', '6',
'7', '8', '9', '10', 'J', 'Q', 'K' or int from 1 to 13)
Raises:
ValueError: If it cannot cast input to a known suit or value.
Returns:
None.
"""
try:
# figure wheather we are using '♥', '♦', '♠', '♣' by symbol or name
suits_map[suit]
except:
self.suit = suit
else:
self.suit = suits_map[suit]
try:
# figure wheather we are using symbols or number for the values
vals_map[str(value)]
except:
self.value = str(value) # typecast if integer
else:
self.value = vals_map[str(value)]
if (self.value not in vals_map.values()) or (self.suit not in suits_map.values()):
raise ValueError('Incorrect suit or value.')
def bj_value(self) -> int:
"""
Method that return the BJ value of the card object.
Returns:
int: BJ value.
"""
return bj_vals[self.value]
def as_str(self, colored=True) -> str:
"""
Method that returns the card objects specs as a string.
Args:
colored (bool, optional): Wheather we want a colored output. Defaults to True.
Returns:
str: playing card value and suit in a single string.
"""
if colored == False:
return self.suit + self.value
if self.suit == '♥' or self.suit == '♦':
color = fg.red + bg.lightgrey
else: # other suits
color = fg.black + bg.lightgrey
return color + self.suit + self.value + colors.reset
class deck():
''' Class that represents a playing cards deck'''
def __init__(self, N_decks=1):
"""
Initializer funtion for deck objects
Args:
N_decks (int, optional): Number of decks. Defaults to 1. Must be a
positive integer. Defaults to 1.
Raises:
ValueError: If N_decks is not a positive integer.
Returns:
None.
"""
if N_decks <= 0 or not isinstance(N_decks, int):
raise ValueError('The number of decks must be a positive integer.')
suits = ['♥', '♦', '♠', '♣']
values = ['A', '2', '3', '4', '5', '6',
'7', '8', '9', '10', 'J', 'Q', 'K']
self.cards = []
for n in range(N_decks):
for suit in suits:
for val in values:
self.cards.append(card(suit, val))
def shuffle(self):
"""
Method that shuffles the deck(s).
Returns:
None.
"""
random.shuffle(self.cards)
def draw(self, N_cards=1) -> card | list[card]:
"""
Method that draws one or more card objects from a
deck object.
Args:
N_cards (int, optional): How many cards to draw. Defaults to 1.
Raises:
ValueError: If N_cards is not a positive integer.
Returns:
card | list[card]: card object or list of card objects. The list is
returned if N_cards>1.
"""
if N_cards <= 0 or not isinstance(N_cards, int):
raise ValueError('The number of cards must be a positive integer.')
if N_cards == 1:
return self.cards.pop()
else:
cds = []
for j in range(N_cards):
cds.append(self.cards.pop())
return cds
class player():
"""Class that represents a (human) BJ player"""
def __init__(self, player_cards: list[card], name: str):
"""
Initializer function for player objects.
Args:
player_cards (list[card]): List of the initial player cards.
name (str): Player's name.
Returns:
None.
"""
self.player_cards = player_cards
self.name = name
self.action = None
self.cash = None
self.bet = None
self.state = None
self.value = None
self.splitted = False
def play(self) -> str:
"""
Method that prompts player to play his hand.
Returns:
str: Players action: 'stay', 'hit', 'double down', 'bust' or 'blackjack'
TODO: add 'split' action.
"""
print(self.name+"'s cards:")
self.value, _ = count_value(self.player_cards)
for c in self.player_cards:
print(c.as_str(), end=colors.reset + ' ')
print('total =', self.value)
if self.action == 'double down':
if self.value > 21:
self.action = 'bust'
print(self.name + ' busts at ' + str(self.value)+'!')
print('--------------------------------------')
return self.action
else:
self.action = 'stay'
print(self.name + ' is forced to stay at', self.value)
print('--------------------------------------')
return self.action
if self.value == 21 and len(self.player_cards) == 2:
print('Blackjack for ' + self.name+'!')
print('--------------------------------------')
self.action = 'blackjack'
return self.action
if self.value > 21:
self.action = 'bust'
print(self.name + ' busts at ' + str(self.value)+'!')
print('--------------------------------------')
return self.action
# or len(self.player_cards) == 1:
if (len(self.player_cards) == 2 and self.player_cards[0].bj_value() != self.player_cards[1].bj_value()) and self.splitted == False:
# different cards or single card after split
tmp = input('Choose an action (h/s/d)\n')
while tmp != 'h' and tmp != 's' and tmp != 'd' and tmp != 'exit':
print(fg.orange + 'Invalid input...' +
colors.reset+'\nTry again,')
tmp = input(
'Choose an action (h/s/d). Type "exit" to quit game.\n')
elif len(self.player_cards) == 2 and self.player_cards[0].bj_value() == self.player_cards[1].bj_value() and self.splitted == False:
# equal cards (split option)
tmp = input('Choose an action (h/s/d/x)\n')
while tmp != 'h' and tmp != 's' and tmp != 'exit' and tmp != 'd' and tmp != 'x':
print(fg.orange + 'Invalid input...' +
colors.reset+'\nTry again,')
tmp = input(
'Choose an action (h/s/d/x). Type "exit" to quit game.\n')
if tmp == 'x':
self.splitted = True
else:
tmp = input('Choose an action (h/s)\n')
while tmp != 'h' and tmp != 's' and tmp != 'exit':
print(fg.orange + 'Invalid input...' +
colors.reset+'\nTry again,')
tmp = input(
'Choose an action (h/s). Type "exit" to quit game.\n')
if tmp == 'exit':
print('Quiting game...')
sys.exit()
self.action = actions[tmp]
print(self.name + ' ' + self.action + 's on', self.value)
print('--------------------------------------')
return self.action
def place_bet(self):
"""
Method that prompts player to place his bet and checks for its
validity.
Returns:
None.
"""
valid_input = False
while (valid_input == False):
tmp = input("Please place your bet:\n"+fg.green + "$")
if tmp == 'exit':
print(colors.reset + "Quiting game...")
sys.exit()
print(colors.reset)
numerical_input = is_numerical(tmp)
positive_input = False
enough_cash = False
if numerical_input == True:
if self.cash - float(tmp) < 0:
enough_cash = False
print(fg.orange + "Insufficient funds."+colors.reset+" You only have " +
fg.green + "$" + str(self.cash) + colors.reset + "...")
elif float(tmp) <= 0:
positive_input = False
print(fg.orange + "Invalid input." + colors.reset +
" A bet has to be more than " + fg.green + '$0' + colors.reset)
elif float(tmp) > 0:
positive_input = True
enough_cash = True
else:
print(fg.orange + "Invalid input." + colors.reset +
" Please enter a valid amount.")
if numerical_input == True and positive_input == True and enough_cash == True:
valid_input = True
self.bet = float(tmp)
self.cash = self.cash - self.bet
class dealer():
''' Class that represents the BJ Dealer.'''
def __init__(self, dealer_cards: list[card]):
"""
Initializer function for the dealer object.
Args:
dealer_cards (list[card]): List of initial cards.
Returns:
None.
"""
self.hidden = dealer_cards[0]
self.dealer_cards = dealer_cards
self.action = None
def play(self) -> str:
"""
Method that playes the dealer's hand'
Returns:
str: Dealer's action: 'stay', 'hit', 'bust' or 'blackjack'
"""
print("Dealer's cards:")
self.value, self.soft = count_value(self.dealer_cards)
for c in self.dealer_cards:
print(c.as_str(), end=colors.reset + ' ')
print('total =', self.value)
if self.value == 21 and len(self.dealer_cards) == 2:
self.action = 'blackjack'
print('Blackjack for the Dealer!')
print('--------------------------------------')
self.action = 'blackjack'
return self.action
if self.value > 21:
self.action = 'bust'
print('Dealer busts at ' + str(self.value)+'!')
print('--------------------------------------')
return self.action
if self.value > 17:
self.action = 'stay'
elif self.value <= 16:
self.action = 'hit'
elif self.value == 17:
if self.soft == True:
self.action = 'hit'
else:
self.action = 'stay'
print("Dealer " + self.action+'s at ' + str(self.value))
print('--------------------------------------')
return self.action
class player_bot():
"""
TODO
"""
def __init__(self):
pass