-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
325 lines (291 loc) · 12 KB
/
main.py
File metadata and controls
325 lines (291 loc) · 12 KB
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
"""
Ben Royce
This is a program to emulate the game of Cribbage.
"computer" or "player"
"""
from Card import Card
from random import shuffle, randint
from itertools import chain, combinations
MAX_SCORE = 121
def request_card_index(message, low, high):
"""
Performs input validation when prompting the user to select a card.
:param message: A string to be displayed to the user when asking for the card. Do not include a trailing space.
:param low: The smallest valid index.
:param high: The largest valid index.
:return: A valid card index given by the user.
"""
while True:
index = input(message + f"Enter a number from {low} to {high}: ")
try:
index = int(index)
except:
print("Please enter an integer.")
continue
if not (low <= index <= high):
print("Please enter a valid integer.")
continue
break
return index
def is_run(cards):
if isinstance(cards, list):
cards.sort(key=lambda c: c.rank)
for i in range(len(cards) - 1):
if cards[i].rank - cards[i + 1].rank != -1:
return False
return True
def restart_game():
while True:
response = input("Would you like to play again? Enter Y/N: ")
if response.upper() == "Y":
main()
elif response.upper() == "N":
print("Goodbye.")
quit()
def add_score(game_obj, points, owner):
if owner == "player":
game_obj.player_score += points
if game_obj.player_score >= MAX_SCORE:
print(f"{MAX_SCORE} points reached! YOU WIN!")
restart_game()
else:
game_obj.computer_score += points
if game_obj.computer_score >= MAX_SCORE:
print(f"{MAX_SCORE} points reached! COMPUTER WINS!")
restart_game()
class Cribbage:
def __init__(self):
self.cards = [Card(r, s) for s in "SCHD" for r in range(1, 14)]
self.deck = self.cards.copy()
shuffle(self.deck)
self.player = []
self.player_score = 0
self.computer = []
self.computer_score = 0
self.crib = []
self.table = []
self.player_is_dealer = True
self.flip_card = None
def pick_first_dealer(self):
while True:
player_choice = self.deck[request_card_index("Pick a card from the deck! ", 1, 52) - 1]
computer_choice = self.deck[randint(0, 51)]
print("The card you chose is", player_choice)
print("The computer chose", computer_choice)
if player_choice.rank == computer_choice.rank:
print("It's a tie! Pick again.")
continue
return player_choice.rank < computer_choice.rank
def deal(self, player_deals):
"""
Deals cards based on the dealer.
"""
# Dealing cards. Non-dealer gets first card.
print("You are the dealer." if player_deals else "Computer is the dealer.")
for i in range(12):
j = i if player_deals else i + 1 # neat trick to flip parity
if j % 2:
self.computer.append(self.deck.pop(0))
else:
self.player.append(self.deck.pop(0))
self.player.sort(key=lambda c: c.rank)
self.computer.sort(key=lambda c: c.rank)
# Contributing cards to crib
target = "your" if player_deals else "Computer's"
print(f"Choose two cards to contribute to {target} crib.")
for i in range(2):
print("Hand:", self.player)
crib_card = request_card_index("", 1, 6 - i)
self.crib.append(self.player.pop(crib_card - 1))
self.crib.append(self.computer.pop(randint(0, 6 - i - 1))) # Computer randomly chooses crib cards
print("Selection:", self.crib[::2]) # Shows only the player's contribution
# Cutting the deck
if player_deals:
cut = randint(1, 39)
print("Computer chooses", cut, "card" if cut == 1 else "cards", "to cut.")
else:
cut = request_card_index("How many cards would you like to cut? ", 1, 39)
print("Cutting", cut, "card." if cut == 1 else "cards.")
self.flip_card = self.deck[cut]
# DEBUG : CUSTOM HANDS
self.flip_card = Card(6, "H")
self.player = [Card(1, "H"), Card(7, "S"), Card(10, "H"), Card(11, "H")]
# Printing information and special Jack rules
print("\nFlip card:", self.flip_card)
if self.flip_card.rank == 11:
print("His heels! (+2)")
if player_deals:
add_score(self, 2, "player")
else:
add_score(self, 2, "computer")
else:
for card in self.player:
if card.rank == 11:
if card.suit == self.flip_card.suit:
print("Nobs! (+1)")
add_score(self, 1, "player")
for card in self.computer:
if card.rank == 11:
if card.suit == self.flip_card.suit:
print("Nobs! (+1)")
add_score(self, 1, "computer")
print("Hand:", self.player)
def peg(self):
player_hand = self.player.copy()
computer_hand = self.computer.copy()
total = 0
def play(player):
nonlocal player_hand, computer_hand, total
if player == 'computer':
while True: # Making sure we get a card that doesn't go over 31
card_index = randint(0, len(computer_hand) - 1)
card = computer_hand[card_index]
if card.value + total <= 31:
break
self.table.append(computer_hand.pop(card_index))
print("Computer plays", card)
add_score(self, get_table_score(), "computer")
else:
while True:
print("Hand:", player_hand)
card_index = request_card_index("Choose a card to play. ", 1, len(player_hand)) - 1
card = player_hand[card_index]
if card.value + total > 31:
print("Total cannot exceed 31. Try again.")
continue
break
self.table.append(player_hand.pop(card_index))
print("You play", card)
add_score(self, get_table_score(), "player")
total += card.value
print("Table:", self.table, "Sum =", total, end="\n\n")
def get_table_score():
score = 0
cards_down = len(self.table)
if cards_down == 1:
return score
if sum(map(int, self.table)) == 15:
print("Fifteen! (+2)")
score += 2
elif sum(map(int, self.table)) == 31:
print("Thirty one! (+2)")
score += 2
if self.table[-1].rank == self.table[-2].rank:
if cards_down > 2 and self.table[-2].rank == self.table[-3].rank:
if cards_down > 3 and self.table[-3].rank == self.table[-4].rank:
print("Quads! (+4)")
score += 12
else:
print("Trips! (+3)")
score += 6
else:
print("Pair! (+2)")
score += 2
if cards_down > 2:
for i in range(cards_down - 2):
if is_run(self.table[i:]):
run_length = cards_down - i
print(f"Run of {run_length}! (+{run_length})")
score += run_length
break
return score
# Non dealer goes first
print("\nLet the pegging begin!")
if self.player_is_dealer:
play("computer")
players_turn = True
# PEGGING FINALLY GOT IT TO ALTERNATE IN A LOOP
while len(player_hand) or len(computer_hand): # while someone has cards left
player_can_play = len(player_hand) and (player_hand[0].value + total <= 31)
computer_can_play = len(computer_hand) and (computer_hand[0].value + total <= 31)
if players_turn and player_can_play:
play("player")
players_turn = not computer_can_play
elif computer_can_play:
play("computer")
players_turn = True
else:
print("End of stack!\n")
self.table.clear()
total = 0
print("Pegging completed!")
print("Computer's score:", self.computer_score)
print("Player's score:", self.player_score)
print()
def scoring(self):
def get_hand_score(hand):
score = 0
card_groups = reversed(list(chain.from_iterable(combinations(hand, r) for r in range(2, len(hand) + 1))))
runs_of_4 = 0
runs_of_5 = 0
flushes = 0
for group in card_groups:
if sum(map(int, group)) == 15:
score += 2
if len(group) == 5:
if len({card.suit for card in group}) == 1:
score += 5
flushes += 1
if is_run(group):
score += 5
runs_of_5 += 1
elif len(group) == 4:
if len({card.suit for card in group}) == 1 and flushes == 0 and self.flip_card not in group:
score += 4
if runs_of_5 == 0 and is_run(group):
score += 4
runs_of_4 += 1
elif len(group) == 3:
if runs_of_4 + runs_of_5 == 0 and is_run(group):
score += 3
elif len(group) == 2:
if group[0].rank == group[1].rank:
score += 2
return score
full_hand_player = sorted(self.player + [self.flip_card], key=lambda c: c.rank)
full_hand_computer = sorted(self.computer + [self.flip_card], key=lambda c: c.rank)
full_hand_crib = sorted((self.crib + [self.flip_card]), key=lambda c: c.rank)
player_score = get_hand_score(full_hand_player)
computer_score = get_hand_score(full_hand_computer)
crib_score = get_hand_score(full_hand_crib)
print(f"The flip card was {self.flip_card}.")
if self.player_is_dealer:
print("Computer's hand:", full_hand_computer)
print("Computer's score:", computer_score)
add_score(self, computer_score, "computer")
print("Your hand:", full_hand_player)
print("Your score:", player_score)
add_score(self, player_score, "player")
print("Your crib:", full_hand_crib)
print("Crib score:", crib_score)
add_score(self, crib_score, "player")
else:
print("Your hand:", full_hand_player)
print("Your score:", player_score)
add_score(self, player_score, "player")
print("Computer's hand:", full_hand_computer)
print("Computer's score:", computer_score)
add_score(self, computer_score, "computer")
print("Computer's crib:", full_hand_crib)
print("Crib score:", crib_score)
add_score(self, crib_score, "computer")
print()
print("You have", self.player_score, "points.")
print("Computer has", self.computer_score, "points.")
print()
self.player.clear()
self.computer.clear()
self.table.clear()
self.crib.clear()
self.deck = self.cards.copy()
shuffle(self.deck)
def main():
game = Cribbage()
player_deals = game.pick_first_dealer()
while True:
game.deal(player_deals)
game.peg()
game.scoring()
player_deals = not player_deals # alternate dealer
if __name__ == '__main__':
main()