forked from fgiorgetti/blackjackpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblackjack.py
executable file
·611 lines (497 loc) · 18.5 KB
/
blackjack.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
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
#!/Users/fgiorgetti/anaconda2/bin/python
from __future__ import print_function
import time
import os
import random
class Deck(object):
def __init__(self):
self.initDeck()
self.current_card_idx = 0
def initDeck(self, number_decks=1):
self.cards = []
i = 0
while (i < number_decks):
# Initializing 10 points cards
for symbol in ['Q', 'J', 'K', '10']:
for suit in Card.suits:
self.cards.append(Card(symbol, suit, 10))
# Pip cards
for symbol in range(2, 10):
for suit in Card.suits:
self.cards.append(Card(str(symbol), suit, int(symbol)))
# Aces
for suit in Card.suits:
self.cards.append(Card('A', suit, 11))
i += 1
def shuffle(self):
for c in self.cards:
c.face_up = True
random.shuffle(self.cards)
self.current_card_idx = 0
def get_next_card(self):
card = self.cards[self.current_card_idx]
self.current_card_idx += 1
return card
class Card(object):
suit_spades = u'\u2660'
suit_clubs = u'\u2663'
suit_hearts = u'\u2665'
suit_diamond = u'\u2666'
suits = [suit_spades, suit_clubs, suit_hearts, suit_diamond]
def __init__(self, symbol, suit, points, face_up=True):
self.symbol = symbol
self.suit = suit
self.points = points
self.face_up = face_up
def printCard(self):
print('%2s%s' % (self.symbol, self.suit), end='')
class Player(object):
state_wait_nxt = 'WAIT_NXT'
state_wait_oth = 'WAIT_OTH'
state_wait_bet = 'WAIT_BET'
state_playing = 'PLAYING'
state_lost = 'LOST'
player_commands = { 'H': 'Hit', 'S': 'Stand', 'V': 'Split', 'D': 'Double down' }
player_states = {
state_wait_nxt: 'Waiting next game',
state_wait_oth: 'Waiting other players',
state_wait_bet: 'Waiting on Bet',
state_playing: 'Playing',
state_lost: 'Lost'
}
def __init__(self, name):
self.name = name
self.money = 1000
self.reset()
def show_hand(self, handidx):
for hand in self.hands:
if hand.insurance > 0:
side_bet_str = '- Insurance bet: %8.2f ' % hand.insurance
else:
side_bet_str = ''
# Show player info
print('%-16s - Hand %d ( %2d - %-10s ) = ' % (self.name, handidx, hand.points, hand.hand_state), end='')
for card in hand.cards:
card.printCard()
print(' ', end='')
print()
print('%23s $%8.2f - Bet $%8.2f %s' % ("Cash", self.money, hand.bet, side_bet_str))
handidx += 1
def reset(self):
self.hands = [] # One or more sets of cards (when splitting)
self.player_state = Player.player_states[Player.state_wait_nxt];
def allowed_commands(self):
cmds = []
if len(self.hands) == 0:
return cmds
cmds.append('H')
cmds.append('S')
has_money = self.money >= self.hands[0].bet + self.hands[0].insurance
# check if split is allowed
if has_money and len(self.hands) == 1 and len(self.hands[0].cards) == 2:
if self.hands[0].cards[0].symbol == self.hands[0].cards[1].symbol:
cmds.append('V')
return cmds
def split(self):
self.hands.append(Hand())
self.hands[1].add_card(self.hands[0].cards.pop())
self.hands[1].bet = self.hands[0].bet
self.hands[1].insurance = self.hands[0].insurance
self.hands[0].update()
self.hands[1].update()
self.money -= self.hands[0].bet + self.hands[0].insurance
def read_command(self, deck):
# Loop through each player's hands
handidx = 0
for hand in self.hands:
# If hand not allowed to play
if hand.hand_state != Hand.state_play:
return
cmds = self.allowed_commands()
# If double-down allowed in this hand
if not hand.double and self.money > hand.bet + hand.insurance:
cmds.append('D')
print('%-16s - Allowed moves' % self.name, end='')
for cmd in cmds:
print(' - [%s] %s' % (cmd, Player.player_commands[cmd]), end='')
handidx += 1
while True:
cmd = raw_input(' HAND[%d] - Enter your move: ' % (handidx))
cmd = cmd.upper()
if cmds.count(cmd) == 0:
print('Invalid command, try again')
continue
else:
break
# If split
if cmd == 'V':
print('Splitting and reading move again')
self.split()
time.sleep(1)
self.read_command(deck)
return
elif cmd == 'D':
print('Doubling down')
self.money -= hand.bet + hand.insurance
hand.double = True
hand.bet *= 2
hand.insurance *= 2
hand.add_card(deck.get_next_card())
if hand.hand_state != Hand.state_busted:
hand.hand_state = Hand.state_stand
elif cmd == 'H':
hand.add_card(deck.get_next_card())
elif cmd == 'S':
hand.hand_state = Hand.state_stand
def bet(self):
self.player_state = Player.player_states[Player.state_wait_bet]
def wait_others(self):
self.player_state = Player.player_states[Player.state_wait_oth]
def lost(self):
self.player_state = Player.player_states[Player.state_lost]
for hand in self.hands:
hand.lost()
class Hand(object):
state_play = 'PLAYING'
state_stand = 'STANDING'
state_busted = 'BUSTED'
state_lost = 'LOST'
state_21 = '21'
state_blackjack = 'BJ'
state_won = 'WON'
state_draw = 'DRAW'
hand_states = {
state_play: 'Playing',
state_stand: 'Standing',
state_busted: 'Busted',
state_lost: 'Lost',
state_21: '21',
state_blackjack: 'BlackJack',
state_won: 'Won',
state_draw: 'Push'
}
def __init__(self):
self.hand_state = Hand.state_play
self.cards = []
self.points = 0
self.bet = 0.0
self.insurance = 0.0
self.double = False
def add_card(self, card):
self.cards.append(card)
self.update()
def update(self):
points = 0
aces = 0
for c in self.cards:
if not c.face_up:
continue
if c.symbol == 'A':
aces += 1
else:
points += c.points
# Adjust value of each ace now
aces_min = aces
aces_hi = 0 if aces == 0 else 11 + (aces - 1)
if points + aces_hi <= 21:
points += aces_hi
else:
points += aces_min
if (points > 21):
self.hand_state = Hand.state_busted
elif (points == 21 and len(self.cards) > 2):
self.hand_state = Hand.state_21
elif (points == 21 and len(self.cards) == 2):
self.hand_state = Hand.state_blackjack
self.points = points
def double(self):
self.double = True
def lost(self):
self.hand_state = Hand.state_lost
class Dealer(object):
hand = Hand()
face_down = True
has_blackjack = False
def reset(self):
Dealer.hand = Hand()
Dealer.face_down = True
Dealer.has_blackjack = False
def show_hand(self):
print()
# Show dealer info
print('%-16s - Hand ( %2d - %-10s ) = ' % ('Dealer', self.hand.points, self.hand.hand_state),
end='')
for card in self.hand.cards:
card.printCard() if card.face_up or not self.face_down else print("???", end='')
print(' ', end='')
print()
@staticmethod
def check_insurance():
has_ace = False
for card in Dealer.hand.cards:
if card.face_up and card.symbol == 'A':
has_ace = True
if has_ace:
if Dealer.hand.points == 21:
Dealer.has_blackjack = True
return True
return False
@classmethod
def play(cls, table, deck):
print('Dealer will play now')
if Dealer.face_down:
Dealer.face_down = False
for card in cls.hand.cards:
card.face_up = True
cls.hand.update()
table.draw_table()
# If not busted and lower than 17
while cls.hand.hand_state == Hand.state_play:
if cls.hand.points >= 17:
cls.hand.hand_state = Hand.state_stand
elif cls.hand.points < 17:
cls.hand.add_card(deck.get_next_card())
table.draw_table()
class Table(object):
state_wait_start = 'Waiting to start'
state_wait_player = 'Waiting on players'
state_wait_bet = 'Waiting on bets'
state_results = 'Match is over'
const_min_bet = 10
def __init__(self):
self.players = []
self.dealer = Dealer()
self.deck = Deck()
self.state = Table.state_wait_start
def number_of_players(self):
while True:
try:
number = int(raw_input('Enter the number of players: '))
except:
print('Enter a positive number of players')
continue
else:
break
idx = 0
while (idx < number):
name = raw_input('Name for player {id}: '.format(id=idx + 1))
self.players.append(Player(name))
idx += 1
self.state = Table.state_wait_bet
def read_bets(self):
for player in self.players:
player.bet()
self.draw_table()
player.hands.append(Hand())
bet = 0;
while True:
try:
bet = int(raw_input('Player [ %16s ] - enter your bet [ min = %5d / max = %5d ]: '
% (player.name, Table.const_min_bet, player.money)))
except:
print('Invalid bet value')
continue
else:
if bet < Table.const_min_bet:
print('Bet value is below min')
continue
elif bet > player.money:
print('You do not have all this money')
continue
else:
break
player.hands[0].bet = bet
player.money -= bet
player.wait_others()
self.state = Table.state_wait_player
def draw_table(self):
os.system("clear")
print('Black Jack Game - Python')
print()
print('Game state -> %s' % (self.state))
if self.state not in [ Table.state_wait_player, Table.state_results ]:
return
# Prints the dealer's hand
self.dealer.show_hand()
for player in self.players:
handidx = 1
player.show_hand(handidx)
print()
time.sleep(1)
def distribute_initial_cards(self):
self.deck.shuffle()
idx = 0
while idx < 2:
for player in self.players:
player.hands[0].add_card(self.deck.get_next_card())
self.draw_table()
dealer_card = self.deck.get_next_card()
dealer_card.face_up = True if idx > 0 else False
self.dealer.hand.add_card(dealer_card)
self.draw_table()
idx += 1
if Dealer.check_insurance():
self.offer_insurance()
def offer_insurance(self):
for player in self.players:
insurance_yn = raw_input('Player [ %16s ] - Do you want insurance (y/N): ' % (player.name))
if 'y' != insurance_yn.lower():
continue
while True:
max_bet = player.hands[0].bet / 2.0
try:
bet = float(raw_input('Player [ %16s ] - Enter your insurance bet [ min = 1 / max = %5d ]: '
% (player.name, max_bet)))
except:
print('Invalid insurance bet value, try again')
continue
else:
if bet < 1 or bet > max_bet:
print("Insurance bet must be between 1 and %8.2f" % (max_bet))
continue
break
player.hands[0].insurance = bet
self.draw_table()
if Dealer.has_blackjack:
print('Dealer has BlackJack!')
Dealer.hand.cards[0].face_up = True
Dealer.hand.update()
self.draw_table()
for player in self.players:
if player.hands[0].hand_state != Hand.state_blackjack:
player.lost()
self.draw_table()
else:
# Insurance bet pays twice
player.money += player.hands[0].insurance * 2
def interact(self):
# Read player commands
has_valid_hands = True
while has_valid_hands:
for player in self.players:
player.read_command(self.deck)
self.draw_table()
has_valid_hands = False
for player in self.players:
for hand in player.hands:
if hand.hand_state == Hand.state_play:
has_valid_hands = True
break
if has_valid_hands:
break
Dealer.play(self, self.deck);
self.draw_table()
## Check results
self.state = Table.state_results
# Check winning hands
winning_hands = [ ]
if Dealer.hand.points == 21 and len(Dealer.hand.cards) == 2:
winning_hands.append(Dealer.hand)
Dealer.hand.hand_state = Hand.state_won
for player in self.players:
for hand in player.hands:
if hand.hand_state == Hand.state_busted:
continue
if hand.hand_state in [Hand.state_blackjack, Hand.state_21] and len(hand.cards) == 2:
# Paying winners
if Dealer.hand.points != 21:
Dealer.hand.hand_state = Hand.state_lost
winning_hands.append(hand)
player.money += hand.bet * 2
else:
player.money += hand.bet
hand.hand_state = Hand.state_draw
Dealer.hand.hand_state = Hand.state_draw
elif Dealer.hand.hand_state == Hand.state_won:
hand.hand_state = Hand.state_lost
# If no blackjack or 21, check highest scores that were not busted
hi_score = 0
if Dealer.hand.points == 21:
self.new_match()
return;
elif Dealer.hand.points > 21:
Dealer.hand.hand_state = Hand.state_busted
else:
hi_score = Dealer.hand.points
# Defining and paying winners
num_losses = 0
for player in self.players:
for hand in player.hands:
if hand.hand_state == Hand.state_busted:
num_losses += 1
continue
winning_hand = hand.points == 21 and len(hand.cards) == 2
if hand.points > hi_score and not winning_hand:
hand.hand_state = Hand.state_won
player.money += hand.bet * 2
elif hand.points == hi_score:
hand.hand_state = Hand.state_draw
player.money += hand.bet
else:
hand.hand_state = Hand.state_lost
num_losses += 1
if Dealer.hand.points < 21 and num_losses > 0:
Dealer.hand.hand_state = Hand.state_won
self.new_match()
def new_match(self):
self.draw_table()
print('Preparing next match...')
time.sleep(5)
self.state = Table.state_wait_bet
self.dealer.reset()
for player in self.players:
player.reset()
#
# Loop info
#
# - Player Bet
# - Dealer distributes 2 cards to the player
# - Dealer gets two cards to himself
# - Dealer shows off one of his cards
# - The other card remains face down (hole card)
# - If the dealer has an ace facing up, then it offers an Insurance bet
# (Basically the player that takes insurance bet, will win 2:1 on the side bet value,
# which cannot exceeds 50% of initial bet, if the dealer has a blackjack)
# - This side bet cannot exceed half of the original's player bet
# - If dealer has a blackjack (ace + any 10 points card), he will show it
# - At this point, all players without insurance bet will lose
# - Players with insurance will lose their initial bet and win 2:1 the insurance bet
# - If player initial bet was 5, the side bet (insurance) will be 2.5.
# - Supposing dealer had blackjack, player wins 5 back and loses initial bet (5).
# - If dealer has blackjack, he wins the match
# - If another player starts with a blackjack, then pot is split between dealer and players with blackjack
# - If player has two cards with same value (10 points card, or any duplicated card), he can split:
# - Split:
# - Player will have 2 hands and 2 parallel games do handle
# - Hit:
# - Player gets an extra card
# - Stand:
# - Player wants to stay with the cards he already has and wait on others
# - Double:
# - Player will get only 1 more card and wait (if not busted)
# - If player busted, he loses his money to the house
# - If player stand or reached 21 / blackjack, he waits till match is over
# - After player stand, dealer will get extra cards till it has less or equal 17 points
#
os.system("clear")
print('Welcome to Python BlackJack!')
table = Table()
table.number_of_players()
while True:
idx = 0
while idx < len(table.players):
player = table.players[idx]
if player.money > 0:
idx += 1
continue
else:
print('Player [ %s ] is out of money and is being removed' %(player.name))
time.sleep(1)
del table.players[idx]
if len(table.players) == 0:
print('Everybody is poor now and house is happy')
print('Game Over')
break
table.read_bets()
table.distribute_initial_cards()
table.interact()