TDT4109/Exercise 5/task11.py

261 lines
6.9 KiB
Python

from common.inputChecking.boolInput import boolInput
from os import get_terminal_size, system
from math import ceil
import random
class Card:
def __init__(self, cardId, color):
"""
cardId goes from 1 to 1 where 1 is A and 13 is K
"""
self.id = cardId
self.color = color
cardNums = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
cardSyms = {
'spade': '',
'heart': '',
'diamond': '',
'club': ''
}
self.num = cardNums[cardId-1]
self.sym = cardSyms[color]
WIDTH = 11
def __str__(self):
return f"""┌─────────┐
{self.num}{' ' if self.id != 10 else ''}
│ │
│ │
{self.sym}
│ │
│ │
{' ' if self.id != 10 else ''}{self.num}
└─────────┘"""
def value(self, aceValue):
cardValues = {
'A': aceValue,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'10': 10,
'J': 10,
'Q': 10,
'K': 10
}
return cardValues[self.num]
class CardHandler:
def __init__(self, cards, printSep=5, aceValue = 1):
self.cards = cards
self.printSeparator = printSep
self.aceValue = aceValue
def addCard(self, card):
self.cards.append(card)
def generateNewCard(self):
cardTypes = range(1,14)
cardColors = ['spade', 'heart', 'diamond', 'club']
self.addCard(Card(random.choice(cardTypes), random.choice(cardColors)))
def _concatenateCards(self, cards):
"""
Concatenate several card objects into an list of sublists
where each sublist contains the strings for the cards to be
printed for a specific horizontal line.
"""
cardStringLists = [(str(card)).split('\n') for card in cards]
cardHeight = len(cardStringLists[0])
linesToPrint = [[] for line in range(cardHeight)]
for cardStringList in cardStringLists:
[linesToPrint[line].append(cardStringList[line]) for line in range(cardHeight)]
return linesToPrint
def printCards(self, cardsPerLine):
"""
Print <cardsPerLine> cards per line, horizontally aligned
"""
splitCards =[[] for _ in range(ceil(len(self.cards)/cardsPerLine))]
for i, card in enumerate(self.cards):
splitCards[i // cardsPerLine].append(card)
SplitCardStrings = [self._concatenateCards(cardList) for cardList in splitCards]
printCardList = lambda cardList: print('\n'.join([ (' ' * self.printSeparator).join(line) for line in cardList ]))
[ printCardList(SplitCardString) for SplitCardString in SplitCardStrings ]
def safePrintCards(self):
"""
Print the amount of cards that there is room for depending on the terminal width
"""
cardWidth = Card.WIDTH
extendedCardWidth = (cardWidth + self.printSeparator)
terminalWidth = get_terminal_size().columns
isRoomForExtraCard = terminalWidth % extendedCardWidth >= cardWidth
cardsPerLine = terminalWidth // extendedCardWidth + (1 if isRoomForExtraCard else 0)
self.printCards(cardsPerLine)
@property
def cardSum(self):
return sum([card.value(aceValue=self.aceValue) for card in self.cards])
@property
def containsAce(self):
return any([True for card in self.cards if card.id == 1])
@property
def containsBlackJack(self):
return any([True for card in self.cards if card.num == 'A']) \
and any([True for card in self.cards if card.value(self.aceValue) == 10])
def emptyCard():
result = Card(1,'spade')
result.num = '?'
result.sym = '?'
return result
class Blackjack:
def __init__(self):
self.handler = CardHandler([])
self.emptyHandler = CardHandler([emptyCard(), emptyCard()])
self.dealerHandler = CardHandler([])
self.gameResults = [0,0]
self.reset()
def generateNewCards(self):
self.dealerHandler.cards = []
self.handler.cards = []
for _ in range(2):
self.dealerHandler.generateNewCard()
self.handler.generateNewCard()
self.emptyHandler.cards[0] = self.dealerHandler.cards[0]
def determineAceValue(self):
if self.handler.cardSum < 11 and self.handler.containsAce: # 11 + 1 = 12, 11 + 11 = 22
self.handler.aceValue = self.dealerHandler.aceValue = 11
else:
self.handler.aceValue = self.dealerHandler.aceValue = 1
def reset(self):
self.generateNewCards()
self.determineAceValue()
def youWin(self):
self.gameResults[0] += 1
print("""
__ __
/\ \ /\ \ __
\ `\`\\\\/'/ ___ __ __ __ __ __/\_\ ___
`\ `\ /' / __`\/\ \/\ \ /\ \/\ \/\ \/\ \ /' _ `\
`\ \ \/\ \L\ \ \ \_\ \\ \\ \ \_/ \_/ \ \ \/\ \/\ \
\ \_\ \____/\ \____/ \ \___x___/'\ \_\ \_\ \_\\
\/_/\/___/ \/___/ \/__//__/ \/_/\/_/\/_/
""")
def youLose(self):
self.gameResults[1] += 1
print("""
__ __ ___
/\ \ /\ \ /\_ \
\ `\`\\\\/'/ ___ __ __ \//\ \ ___ ____ __
`\ `\ /' / __`\/\ \/\ \ \ \ \ / __`\ /',__\ /'__`\
`\ \ \/\ \L\ \ \ \_\ \ \_\ \_/\ \L\ \/\__, `\/\ __/
\ \_\ \____/\ \____/ /\____\ \____/\/\____/\ \____\\
\/_/\/___/ \/___/ \/____/\/___/ \/___/ \/____/
""")
def gameOver(self, gameWon):
system('clear')
print('\nDEALERS CARDS\n')
self.dealerHandler.safePrintCards()
print('\nYOUR CARDS\n')
self.handler.safePrintCards()
print()
print('Ace value is', self.handler.aceValue)
if gameWon:
self.youWin()
else:
self.youLose()
print()
print(f'Wins: {self.gameResults[0]} Losses: {self.gameResults[1]}')
if boolInput(
'Do you want to play again? [y/n]: ',
yesNoLetters=['y','n'],
error=''
):
self.reset()
else:
exit(0)
def checkIfLost(self):
winningConditions = [
self.handler.containsBlackJack and not self.dealerHandler.containsBlackJack,
self.dealerHandler.cardSum > 21
]
losingConditions = [
self.handler.cardSum > 21
]
if any(losingConditions):
self.gameOver(gameWon=False)
return True
elif any(winningConditions):
self.gameOver(gameWon=True)
return True
return False
def update(self):
system('clear')
print('\nDEALERS CARDS\n')
self.emptyHandler.safePrintCards()
print('\nYOUR CARDS\n')
self.handler.safePrintCards()
print()
print('Ace value is', self.handler.aceValue)
print()
if not self.checkIfLost():
if not boolInput('Continue? [y/n]: ', yesNoLetters=('y','n')):
gameWon = self.dealerHandler.cardSum < self.handler.cardSum
self.gameOver(gameWon=gameWon)
return
self.handler.generateNewCard()
def loop(self):
while True:
self.update()
if __name__ == "__main__":
game = Blackjack()
game.loop()