I am having trouble using one function in another to deal cards. Here is what I have so far.
import random as rand
def create():
ranks = [\'2\', \'3\',
Forget about your two functions, since they're both correct, and look at your top-level code:
import random as rand
def ...
def ...
print(cards_dealt(5, 3, deck))
Where does deck
come from? Nowhere. Hence the exception.
It's pretty obvious where you intended it to come from—you have a create
function that ends with a return deck
. You just aren't calling it anywhere.
So you want:
import random as rand
def ...
def ...
deck = create()
print(cards_dealt(5, 3, deck))
… or, if you want to make it clearer that the global variable is unrelated to the two locals:
import random as rand
def ...
def ...
mydeck = create()
print(cards_dealt(5, 3, mydeck))
… or, if you want to make things more concise:
import random as rand
def ...
def ...
print(cards_dealt(5, 3, create()))
Beyond this problem, cards_dealt
doesn't have a return
, so it just returns None
. When your function is complete, there might be something worth printing out, but for now, while you're debugging what you have so far, it's not very useful.
Meanwhile, if you want to print the shuffled deck, which could be useful, you'd do something like this:
import random as rand
def ...
def ...
mydeck = create()
cards_dealt(5, 3, mydeck)
print(mydeck)
When you later finish the function so it returns, say, a tuple of 3 lists of 5 cards, you'll probably want to store those return values for later as well as printing them, so it might go something like this:
import random as rand
def ...
def ...
mydeck = create()
hands = cards_dealt(5, 3, mydeck)
for hand in hands:
print(hand)
Let me suggest an object-oriented approach where we will define the classes Card
, Deck
and Player
.
Using objects instead of lists of cards will provide a neat API to implement games. As you implement game logic, it will also make it easier to keep a single source of truth as to where each card is and which player has each card.
import random
class Card:
def __init__(self, kind, rank, deck):
self._kind = kind
self._rank = rank
self.deck = deck
self.where = None
def __repr__(self):
return 'Card(kind={}, rank={}'.format(self.kind, self.rank)
@property
def kind(self):
return self._kind
@property
def rank(self):
return self._rank
class Deck:
def __init__(self):
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
kinds = ['H', 'C', 'D', 'S']
self.cards = [Card(kind, rank, self) for kind in kinds for rank in ranks]
def deal(self, players, n=None):
if any(p.deck is not self for p in players):
raise RuntimeError('Player {} is not playing the deck'.format(p.id))
n = n if n is not None else len(self.cards)
random.shuffle(self.cards)
for i, card in enumerate(self.cards[:n * len(players)]):
card.where = players[i % len(players)]
class Player:
def __init__(self, id, deck):
self.id = id
self.deck = deck
@property
def hand(self):
return [card for card in deck.cards if card.where is self]
deck = Deck()
players = [Player(id, deck) for id in range(3)]
deck.deal(players, n=4)
for p in players:
print(p.hand)
[Card(kind=D, rank=A), Card(kind=D, rank=2), Card(kind=S, rank=5), Card(kind=S, rank=K)]
[Card(kind=S, rank=9), Card(kind=D, rank=5), Card(kind=C, rank=A), Card(kind=C, rank=Q)]
[Card(kind=C, rank=9), Card(kind=S, rank=J), Card(kind=D, rank=3), Card(kind=H, rank=9)]
The card.where
attribute can be updated to indicate the position of a card. Since it is the single source of truth for card position, this updates the player.hand
property.
deck = Deck()
players = [Player(id, deck) for id in range(2)]
deck.deal(players, n=1)
players[0].hand[0].where = players[1]
for p in players:
print(p.hand)
[]
[Card(kind=H, rank=K), Card(kind=D, rank=2)]
The above API provides the basics to deal cards and move cards from hand to hand, but can be extended to implement new features.
There are certainly other ways to do it, but you could complete your function as so, in order to return a dictionary of your players and their hands:
def cards_dealt(num_cards, num_players, deck):
rand.shuffle(deck)
return {player:[deck.pop() for _ in range(num_cards)] for player in range(num_players)}
Then, create your deck (that is where your "deck is not defined" problem comes in) and call your function:
my_deck = create()
print(cards_dealt(5, 3, my_deck))
Which returns:
{0: [['10S'], ['8S'], ['7D'], ['6S'], ['4C']],
1: [['JD'], ['AC'], ['QD'], ['2D'], ['JS']],
2: [['6D'], ['4H'], ['AS'], ['4S'], ['9S']]}
The equivalent cards_dealt
function, but with loops instead of the dict and list comprehensions is:
def cards_dealt (num_cards, num_players, deck):
rand.shuffle(deck)
player_hands = {i:[] for i in range(num_players)}
for player in range(num_players):
for num in range(num_cards):
player_hands[player].append(deck.pop())
return player_hands