I'm new to Python and coding in general and am trying to create a blackjack game in Python but I'm having trouble getting the point counter point_selection to update based on the card values in the player's hand:
deck_points = {2 : 2, 3 : 3, 4 : 4, 5 : 5, 6 : 6, 7 : 7, 8 : 8, 9 : 9, 10 : 10, 'J' : 10, 'Q'
: 10, 'K' : 10, 'A' : 11 }
dealer_hand = []
player_hand = []
dealer_points = 0
player_points = 0
def deal_initial_cards(hand, point_selection):
for i in range(2):
i = random.choice(list(deck_points))
hand.append(i)
for card in hand:
point_selection += deck_points[card]
deal_initial_cards(dealer_hand, dealer_points)
print(dealer_points)
Using the above code, the counter never updates past '0' and I'm not sure what I'm doing wrong. Any help is appreciated.
Here's a more Pythonic solution that fixes your main bug about your function deal_initial_cards() operating on a copy of its (immutable) int argument point_selection then throwing away the result, since it doesn't have a return point_selection (or store the result in a class member self.points). (Also, I made all your dict keys strings: '2','3',.... It's usually customary to represent '10' as 'T' to make all cards a single letter).
But since you're essentially declaring a Hand class, then instantiating two objects of it (dealer_hand, player_hand). deal_initial_cards() is essentially a Hand._init__() in disguise, so we have a data member cards (best not to also call it hand). See how simple and clean ph = Hand() is; no need for globals. Moreover, we could statically compute points inside the __init__() function and return it (or, better, store it in self.points inside each hand object), but that would be a bad decomposition, since if we subsequently add cards to self.cards, points wouldn't get updated. So, much more Pythonic is to make points a property of the class. We then access it
without parentheses: dh.points, not dh.points(). Last, note the use of a list comprehension (instead of a for-loop-append) to generate self.hand. And inside points(), note the use of a generator expression deck_points[card] for card in self.cards, again more Pythonic than for-loop counter. So this small example is a great showcase of Python idiom and decomposition. We can add a __str__() method to Hand. (You could also have a Card class, but really that would be overkill.)
import random
deck_points = {'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'T': 10, 'J' : 10, 'Q'
: 10, 'K' : 10, 'A' : 11 }
# No globals, so no need to initialize anything
class Hand:
def __init__(self):
self.cards = [random.choice(list(deck_points)) for _ in range(2)]
#property
def points(self):
return sum(deck_points[card] for card in self.cards)
def __str__(self, join_char=''):
return join_char.join(card for card in self.cards)
#deal_initial_cards(dealer_hand, dealer_points)
ph = Hand()
dh = Hand()
print('Dealer: ', end='')
print(dh.points)
print('Player: ', end='')
print(ph.points)
Python ints are immutable, this means dealer_points isn't updated. Initialy they have the same id(use id()), but when you change the variable inside the function it creates a new variable. To fix your code you need to do something like
deck_points = {2 : 2, 3 : 3, 4 : 4, 5 : 5, 6 : 6, 7 : 7, 8 : 8, 9 : 9, 10 : 10, 'J' : 10, 'Q'
: 10, 'K' : 10, 'A' : 11 }
dealer_hand = []
player_hand = []
dealer_points = 0
player_points = 0
def deal_initial_cards(hand, point_selection):
for i in range(2):
i = random.choice(list(deck_points))
hand.append(i)
for card in hand:
point_selection += deck_points[card]
return point_selection
dealer_points = deal_initial_cards(dealer_hand, dealer_points)
print(dealer_points)
whereas a list, which you probably noticed, is mutable. This means the list inside the function stays the same(keeps its id) even when its edited.
Related
I am trying to generate a blackjack game but I cannot get the values my_score and computers_score to update. They keep giving me value of 0. Can anyone advise?
Code is below. I am expecting the sums of the picked cards for my hand and computer's hand to correspond to the scores, but I am getting return values of 0.
import random
# Create the deck and two empty hands
cards = [11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]
my_hand = []
my_score = 0
computers_hand = []
computers_score = 0
# Choose two cards from deck randomly and place into each hand
def hand_builder(chosen_hand, chosen_score):
pick_card = random.choice(cards)
chosen_score = 0
chosen_hand.append(pick_card)
chosen_score += pick_card
for n in range(2):
hand_builder(my_hand,my_score)
hand_builder(computers_hand,computers_score)
print(my_hand)
print(my_score)
print(computers_hand)
print(computers_score)
You have to return your values:
import random
# Create the deck and two empty hands
cards = [11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]
my_hand = []
my_score = 0
computers_hand = []
computers_score = 0
# Choose two cards from deck randomly and place into each hand
def hand_builder(chosen_hand, chosen_score):
pick_card = random.choice(cards)
chosen_score = 0
chosen_hand.append(pick_card)
chosen_score += pick_card
return chosen_score
for n in range(2):
# save the output from the function
my_score += hand_builder(my_hand,my_score)
computers_score += hand_builder(computers_hand,computers_score)
print(my_hand)
print(my_score)
print(computers_hand)
print(computers_score)
The reason that the _hand variable is updated is because it is a list. One can update a list in a function without returning a value.
Please read for exmaple: https://www.mygreatlearning.com/blog/understanding-mutable-and-immutable-in-python/
Or use google to look into mutable and immutable variables.
I am in the process of doing blackjack, but I have a problem.
I don't know how to do that if I want him to print for example the Q to print it as str, but when adding the player's hand I take it as a 10.
The error it gives me is: TypeError: unsupported operand type(s) for +: 'int' and 'str'
The code I have is:
jugador = []
dealer = []
deck = ['A' ,2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'K', 'Q']
for card in deck:
if card == 'J':
card = 10
elif card == 'K':
card = 10
elif card == 'Q':
card = 10
while len(jugador) != 2:
random.choice(deck)
card = random.choice(deck)
jugador.append(card)
if card == 'A':
card = 11
print(jugador)
if sum(jugador) > 11:
print("Hola")```
There are two values you want to track: Name and effective value. Your code currently tries to track them in one variable. Try using either different variables to track Name vs Value or try defining and using a class to capture these details.
You need to upgrade some things in your code for example the deck contains 4 cards of each number, so you should add that. You can also use a dictionary to represent the deck :
deck = {'A': 1, '2': 2, ..., 'K': 10}
Note that in the dictionary the keys are strings and their values are integers. You should also remove the first random.choice(deck) because you stored the random value in the card variable and dont forget to remove the card you picked for the player.
This is a function.
def cut_dk():
a = []; b = []
random.shuffle(deck__)
slice = deck__[:2]
a.append(slice[0])
b.append(slice[1])
return a,b
c = cut_dk()
p1 = c[0]
p2 = c[1]
I have this function at the top of the program along with other functions.
It draws from a predefined list.
When calling this function dynamically in the program it returns the same variables.
It's the cutting of a card deck (two cards, highest wins the draw), when the cards are equal it needs to draw again (this is the problem, a second draw), a new selection from the list, yet it just repeats the variables it has in memory.
Calling the function again in a conditional statement just returns the same initial variables acquired on the first run, so I am unable to repeat the cut dynamically as part of the game play.
I would manage my deck as an object here with a class. This would allow us to define a deck, which is stored as an object and perform multiple functions against the deck whilst retaining the state changes from different functions.
class deck:
"""
Class designed to manage deck including iterations of changes.
Shuffling the deck will retain shuffle changes
"""
def __init__(self):
self.deck = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'J', 'Q', 'K', 'A']
self.original = self.deck[:]
def shuffle(self):
"""
Shuffle deck in-place. self.deck will be modified
"""
random.shuffle(self.deck)
def cut(self):
"""
Shuffles deck and draws the two top-most cards
Returns: tuple(2) two cards from top of the deck
"""
self.shuffle()
a, b = self.deck[:2]
return a, b
def highest_draw(self):
"""
Calls self.cut() to shuffle and retrieve 2x top-most cards.
If cards match the deck is shuffled and cut again.
Returns: 2 Unique cards from top of the deck
"""
a, b = self.cut()
while a == b:
a, b = self.cut()
return a, b
def reset(self):
self.deck = self.original[:]
game = deck()
game.deck
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 'J', 'Q', 'K', 'A']
game.shuffle()
game.deck
#['A', 7, 5, 9, 8, 'J', 'K', 6, 4, 3, 1, 'Q', 2]
game.reset()
game.deck
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 'J', 'Q', 'K', 'A']
game.cut()
#('A', 'Q')
game.highest_draw()
#('J', 2)
You would still need to define how you determine the "highest" card, however this is dependant on your deck, which you have left out of the question.
It sounds as if a generator function would be useful here. You call the function once to set up your iterator and then 'draw' cards from that iterator (in this case the iterator is 'cards'). Notice you have to catch the case where you run the whole deck and it's tied throughout. I've sprinkled print statements through this to make it easier to understand how generators work.
import random
deck__ = list(range(3))*2
def draw_from_shuffled():
random.shuffle(deck__)
print(f'Deck after shuffling: {deck__}')
for c in deck__:
yield c
cards = draw_from_shuffled() #cards is now an iterator
while True:
try:
a = next(cards)
b = next(cards)
except StopIteration:
print(f'End of deck!')
cards = draw_from_shuffled()
continue
print(f'Drew {a} and {b}')
if a != b:
break
print('Hand done.')
Sample output:
Deck after shuffling: [2, 2, 1, 1, 0, 0]
Drew 2 and 2
Drew 1 and 1
Drew 0 and 0
End of deck!
Deck after shuffling: [0, 0, 2, 2, 1, 1]
Drew 0 and 0
Drew 2 and 2
Drew 1 and 1
End of deck!
Deck after shuffling: [0, 2, 1, 0, 1, 2]
Drew 0 and 2
Hand done.
More on generators: https://realpython.com/introduction-to-python-generators/
score = {
'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, 'f': 4,
'g': 2, 'h': 4, 'i': 1, 'j': 8, 'k': 10, 'l': 1,
'm': 2, 'n': 1, 'o': 1, 'p': 3, 'q': 8, 'r': 1,
's': 1, 't': 1, 'u': 1, 'v': 4, 'w': 10, 'x': 10,
'y': 10, 'z': 10,
}
x = input('Enter a Word: ')
y = list(x)
a = []
for i in x:
z = int(score[i])
a = sum(z)
print(a)
a= sum(z) is keep on saying it is "int' object is not iterable".
What did I do wrong?
I am guessing you want a to store the sum of the letters in the word. If so, what you are doing is wrong. sum() function expects an iterable, and then it takes the sum by adding each element of that iterable and returns the sum. Example -
>>> sum([1,2,3,4])
10
It does not work like you are expecting it to.
In your case , you do not even need to use sum, simply do -
a = 0 #before the loop
and
a += z #inside the loop
And also you should indent the print a outisde the for loop, so that you only print the final sum of letters.
As said #Amadan in the comments of #AnandSKumar answer, you could do the following :
a = sum(score[c] for c in x)
Supposing you are still learning Python, here is a explication of what does the above code.
First as explained #AnandSKumar, the sum built-in function takes as parameter an iterable. What is an iterable ? It's an object that implement the __iter__ method which allows to iterate over the data of the object. The best example is a list object. Considere the following code :
my_list = ['a', 'b', 'c']
try:
while True:
print next(my_list)
except StopIteration:
print "The end."
# Result :
a
b
c
The end.
We iterate over the list my_list. The sum function could/would looks like this in Python implementation :
def sum(iterable):
iterator = iter(iterable)
result = next(iterator, 0)
try:
while True:
result += next(iterator)
except StopIteration:
pass
return result
A good approche of what you are doing could be to create a list as following, and the sum it :
a = [score[c] for c in x]
print sum(a)
# Equivalent code (one liner) :
print sum([score[c] for c in x])
# Equivalent code :
a = []
for c in x:
a.append(c)
print sum(a)
The thing is that you generate entirely your list a and it takes memory. The approche of what #Amadan said is to generate dynamically the list a using a generator expression. The idea is to create an iterable, which has no representation in memory.
print sum(score[c] for c in x)
# Equivalent code :
result = 0
for c in x:
result += score[c]
print result
This is more memory efficient and looks like you're a god of Python :)
The one-liner of what you are trying to do :
# Python 3
print sum(score[c] for c in input("Enter a word: "))
# Python 2
print sum(score[c] for c in raw_input("Enter a word: "))
This is the final product. IF anyone else has any tips to cut it up, please let me know! Thanks a lot for the help!
def triple_cut(deck):
''' (list of int) -> NoneType
Modify deck by finding the first joker and putting all the cards above it
to the bottom of deck, and all the cards below the second joker to the top
of deck.
>>> deck = [2, 7, 3, 27, 11, 23, 28, 1, 6, 9, 13, 4]
>>> triple_cut(deck)
>>> deck
[1, 6, 9, 13, 4, 27, 11, 23, 28, 2, 7, 3]
'''
joker1 = deck.index(JOKER1)
joker2 = deck.index(JOKER2)
first = min(joker1, joker2)
first_cards = []
for cards in range(len(deck[:first])):
cards = 0
pop = deck.pop(cards)
first_cards.append(pop)
joker1 = deck.index(JOKER1)
joker2 = deck.index(JOKER2)
second = max(joker1, joker2)
second_cards = []
for cards in deck[second + 1:]:
pop = deck.pop(deck.index(cards))
second_cards.append(pop)
second_cards.reverse()
for card in second_cards:
deck.insert(0, card)
deck.extend(first_cards)
raah I need to type more because my post is mostly code: please add more details sss ss
A hint:
p = list('abcdefghijkl')
pivot = p.index('g')
q = p[pivot:] + p[:pivot]
List slicing is your friend.
answer to the second revision
when the function is finished, it won't mutate the deck.
The problem is that you didn't give the full code. I'm guessing it looks like this:
def triple_cut(deck)
…
deck = q
And according to your docstring you call it as
deck = […]
triple_cut(deck)
and have gotten confused that the assignment deck = q doesn't propagate out of triple_cut(). You can't modify the formal parameters of a method so the assignment remains local to triple_cut and does not affect the module level variable deck
The proper way to write this is
def triple_cut(cuttable)
…
return cuttable[first:] + cuttable[:first]
deck = […]
deck = triple_cut(deck)
where I changed the name of the argument to cuttable to for purposes of explanation. You could keep the argument name as deck but I wanted to show that when you thought you were assigning to deck you were really assigning to cuttable and that assignment wouldn't carry out of triple_cut().