Unsure of how to use attributes and methods in python - python

I am currently working on an assignment where in a particular question I have to take a list of playing cards and, using a class, figure out if it is a Royal Flush.
The lecturer provided a 'skeleton' of code that I have to build the rest around without changing the parts he wrote.
#Lecturer created...
class PokerHand(Hand):
def __init__(self, cards = list()):
Hand.__init__(self, cards)
self.handRank = 0
self.hand = "High Card"
#I have so far added this part...
total_value = 0
val_card_b4 = 0
for card in self.cards:
if Card.getValue(card) > val_card_b4:
total_value += Card.getValue(card)
val_card_b4 = Card.getValue(card)
checkRoyalFlush()
#...to here. However it throws an error that checkRoyalFlush isn't defined.
#The lecturer then had what is below already added.
def checkHand(self):
if self.checkRoyalFlush():
self.handRank = 9
self.hand = "Royal Flush"
print("Test")
I have already created a Card class in an earlier question that allows me to create a card object get the value of the card (A=11, 2-10 equal face value etc.)
My problem is that, once I have checked the cards, I don't know how to 'activate' the if self.checkRoyalFlush(): statement in the checkHand Method.
The code I have to get running is:
h1 = PokerHand([Card('hearts', '10'), Card('clubs', '10'),Card('hearts', '2'),Card('hearts', '3'),Card('spades', 'J')])
h1.show()
print(h1.checkHand())
I would like to understand how to get the if statement working, as I have spent a lond time researching and can't figure it out. I am only a beginner in python and new to the Object Oriented side of it.
Edit: I also don't know how to define 'checkRoyalFlush' without it getting more errors

An if statement such as if self.checkRoyalFlush(): requires a boolean data type as a result, i.e. True or False. Your method needs to return either one of those values:
#Lecturer created...
class PokerHand(Hand):
def __init__(self, cards = list()):
# etc...
def checkHand(self):
# etc...
# add your new method below the methods that already exist
def checkRoyalFlush(self):
# paste your code to check if it is a royal flush here
# if it is a royal flush, then:
return True
# if it is NOT a royal flush, then:
return False
Also you need to refer to your method as self.checkRoyalFlush() as it is a part of the class PokerHand. You aren't doing that in checkHand() method.

It looks like your lecturer want method called checkRoyalFlush() which I'm assuming will return true if your hand is a royal flush or false if it isn't aren't.
Also note that I don't know how you set up your card class, and I don't know what you are calling the suit or value attribute. In the code below, I call .suit for the suit attribute and .value and the value attribute. Change it to whatever you made it as.
Consider code below:
class PokerHand:
def __init__(self, cards = list()):
#your init goes here as above
def checkHand(self):
#checkHand as above
def checkRoyalFlush(self):
check_suit = cards[0].suit #note I don't know what you are calling the suits and values in your hand,
values = ['A','K','Q','J','10'] #values we want to check against
for each_card in cards:
if not (each_card.typing == check_suit and each_card.value in values):
return False
values.remove(each_card.value) #once we found a value we want to remove it from the possible list
return True
The method checkRoyalFlush() will take one card's suit out of the cards list. Since a royal flush must have all the same suit it doesn't matter which card I choose. Here I choose the first card in the list.
Then I iterate through the cards list and check if NOT each of the card's typing is the same, and if each of the values are in the values list
if one card is does not match the requirement, it returns False.
We remove the value we checked so we can make sure it's 1 value and not duplicated values.
If the for loop checking is finished with out returning False, we know that it's a royal flush
Note this is not the most optimal way to do it, it's just one way that shows how it can be done rather clearly.

Related

How do I affect the contents of multiple dictionaries or objects iteratively from within a loop?

I'm trying to use a pen-and-paper RPG system I know as a springboard for learning Python. I want to be able to use an object to represent a character, with all the stats that implies. I'm trying to use for and while loops to do things like determine number of actions per turn (based on individual speed) and the actions they take. My problem is that I can't figure out how to refer to the object within the loop so that Python goes through and sequentially affects each of the objects the way I'd want.
class char:
counter = 0 #char counter to keep track of the nbr of doodz
Asidecounter = 0
Bsidecounter = 0
def __init__(self,name,side,Spd,cSPD,DEF,HP,cHP):
self.name=name
self.side=side
self.Spd=Spd
self.cSPD=cSPD
self.DEF=DEF
self.HP=HP
self.cHP=cHP
char.counter+=1
if self.side == "a":
char.Asidecounter+=1
else:
char.Bsidecounter+=1
activechars.append(name)
activechars=[]
defeatedchars=[]
Okay, this gives us a character and some stats. (I tried to omit some extra stats here.) I have a counter for which side they're on, a total character count and so on every time I add a character to the mix. The problem is, then I want to determine the total # of moves available to be distributed among however many characters, I try to do something like this and it just doesn't work for me:
movecount=0
for i in range(len(activechars)):
movepick = activechars[i]
movecount+=movepick.Spd
You can see where I'm going here. I'd like to do something similar for attacks, special powers usage, whatever--put it in a loop but then call (and change) object-specific values from inside the loops, and I just don't know how to.
I've tried this with dictionaries instead of defined "char" objects; e.g.
hero={"Spd":4,"cSPD":4,"DEF":8,"HP":10,"cHP":10}
...and then rewrite the loops for a dictionary instead. But it's not working either way.
(Also, I've been told that I don't follow Python capitalization conventions, for which I apologize.)
You're so close, it appears to be a small issue. Running your code and created char objects, I noticed you're saving the object's name attribute only into the list - for instance, "lancelot" instead of something like <__main__.char at 0x1e5770a9f48>. Simply change the last line to append self instead of name (also check indentation).
class char:
counter = 0 #char counter to keep track of the nbr of doodz
Asidecounter = 0
Bsidecounter = 0
def __init__(self,name,side,Spd,cSPD,DEF,HP,cHP):
self.name=name
self.side=side
self.Spd=Spd
self.cSPD=cSPD
self.DEF=DEF
self.HP=HP
self.cHP=cHP
char.counter+=1
if self.side == "a":
char.Asidecounter+=1
else:
char.Bsidecounter+=1
activechars.append(self)
activechars=[]
defeatedchars=[]

How to deal with NoneType error in Python?

I have only picked up coding and Python in the past week so this question may seem obvious.
I am trying to create a card game. I have already created my deck of cards and now I'm trying to make a function to remove a card from the deck and to test that I am printing out the deck of cards.
However, when I remove a random card from the deck and then try to print out the properties of the remaining cards in the deck I receive an error.
I created a class called Card with attributes name, value, suit
I took out parts of the code which were very long and not exactly relevant.
I have a list of all my cards called the_deck
I tried to account for the error with
"if the_deck[i] is None:continue:"
but that doesn't seem to work.
Here is my code and then the error.
def pick_a_card():
a = random.choice(the_deck)
print(a.name + " of " + a.suit)
the_deck = the_deck.remove(a)
i = 0
while i <= 51:
if the_deck[i] is None:
continue
else:
print(the_deck[i].name + " of " + the_deck[i].suit)
i+=1
pick_a_card()
The error I get is
TypeError: 'NoneType' object is not subscriptable
This error comes from removing a card from the deck. The rest of the function has no errors.
How do I fix this?
Thanks.
I am not sure what your data looks like but this is an example of how to remove a random from a list that may help.
the_deck = [*range(1, 53, 1)]
def remove_val(the_deck):
a = random.choice(the_deck)
the_deck.remove(a)
print('Value removed:', a)
print('New deck:', the_deck)
A point of note in your example is that, that #alaniwi pointed out, is that should not re-assign the_deck = the_deck.remove(a) rather it should read the_deck.remove(a). Also, you are not handling the fact that len(the_deck) reduces by 1 every time you remove a card.
The remove list method removes the element passed to by modifying the list. That is, it just modifies the list referenced by the list name. However it returns None. If there is no return statement in function/method, Python interpreter returns None.
To check that remove method returns None, run the following code at Python interpreter:
>>> a = [1, 2 ,3]
>>> if a.remove(1) == None:
... print('Returns None')
...
the if block will be executed.
You can also check what type of error Python will return if you try to index and int or None object.
>>> a = None
>>> a[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
Solutions to every issue:
treat remove() as an action, not a parser
use fstrings to concat string data
separate game logic from diagnostics
name things what they are
use a for loop to iterate the deck so you never have to worry about how many cards there are
The below script is an example of how to implement the solution list
from collections import namedtuple
import random
#a simple deck generator so this example is complete
Card = namedtuple('Card', 'face suit')
def deck_generator():
faces = [*list(map(str, range(2,11))), 'J', 'Q', 'K', 'A']
suits = ['♠', '♥', '♦', '♣']
deck = []
for s in suits:
for f in faces:
deck.append(Card(f, s))
return deck
Deck = deck_generator()
#end deck generator
the_deck = Deck[:] #unique copy so you don't have to keep making the deck
#game logic
def pick_a_card():
card = random.choice(the_deck)
the_deck.remove(card) #action not parser
return card #not 'a'
#diagnostic separated from game logic
def viewdeck():
deck = ''
for card in the_deck: #number of remaining cards is irrelevant
deck = f'{deck}{card.face}{card.suit}, ' #fstring
print(deck)
card = pick_a_card()
print(f'{card.face}{card.suit}') #fstring
#5♣
viewdeck()
#2♠, 3♠, 4♠, 5♠, 6♠, 7♠, 8♠, 9♠, 10♠, J♠, Q♠, K♠, A♠,
#2♥, 3♥, 4♥, 5♥, 6♥, 7♥, 8♥, 9♥, 10♥, J♥, Q♥, K♥, A♥,
#2♦, 3♦, 4♦, 5♦, 6♦, 7♦, 8♦, 9♦, 10♦, J♦, Q♦, K♦, A♦,
#2♣, 3♣, 4♣, 6♣, 7♣, 8♣, 9♣, 10♣, J♣, Q♣, K♣, A♣,
Aside
Most container types have inline methods that work on the container directly. These methods may not return anything. You could perceive this as an action you are telling the container to perform on itself. Knowing if an inline method will or will not have a return comes with experience or a quick trip through some docs. As an example, consider remove(), append() or insert() versus pop(). All of these are inline methods, but only pop() returns data.

Having troubles with OOP-style Blackjack sim - AttributeError: 'Deck' object has no attribute 'fulldeck'

I'm trying to learn the principles of OOP through creating a Blackjack game that someone can play through a cli.
I've successfully set up a full deck of 52 cards with the proper number per suit, but I'm having trouble getting a player's hand.
Code I've written already:
import random
class Deck:
def makeCards(self):
self.suits = ['Clubs', 'Diamonds', 'Spades', 'Hearts']
self.rank = ['2','3','4','5','6','7','8','9','10','J','Q','K','A']
for suit in self.suits:
for rank in self.rank:
self.fulldeck = (suit, rank)
self.fulldeck = list(self.fulldeck)
return(self.fulldeck)
def playerHand(self):
self.cardone = random.choice(self.fulldeck)
self.cardtwo = random.choice(self.fulldeck)
print(self.cardone, self.cardtwo)
Deck().makeCards()
Deck().playerHand()
I'd expect this to output 2 random cards from the cards defined in self.fulldeck, but instead it gives me
AttributeError: 'Deck' object has no attribute 'fulldeck'
I don't get why, as I've defined self.fulldeck in the makeCards function.
The main part of your issue is that you are calling methods on two separate instances:
Deck().makeCards() # create a Deck instance, call makeCards on it, and discard it
Deck().playerHand() # create a new Deck instance, call playerHand on it, and discard it
Save a reference to one instance and then reuse it:
a = Deck()
a.makeCards()
a.playerHand()
I also suspect you will have a problem here:
for suit in self.suits:
for rank in self.rank:
self.fulldeck = (suit, rank)
self.fulldeck = list(self.fulldeck)
return(self.fulldeck)
You're iterating through a nested loop, but on every iteration you're reassigning a brand-new self.fulldeck, so only the last one will remain. However, the first iteration is the last one, because you return on it. When you called this function, you didn't even save it - there's no need to return here at all.
You may want something like this instead:
self.fulldeck = []
for suit in self.suits:
for rank in self.rank:
self.fulldeck.append([suit, rank])
You can also refine and fix playerHand as follows:
def playerHand(self):
return random.sample(self.fulldeck, 2)
Your current version chooses a random card twice, with replacement. The card you choose with random.choice is not removed from the deck, so you could get the same card twice. Using random.sample will return two random cards that are guaranteed not to be the same card. You can do whatever you like with these cards, such as print them (print(a.playerHand())).

How to loop through a set, while removing items from the set in Python 3

Here is my situation:
I have a list/set (doesn't matter which) of movieplayer objects that I want to call a "preload" function on. This preload function could return immediately but would like return a bit in the future.
I want to store this collection of movieplayers, indicating that they have not been preloaded yet, then loop through them, calling the preload function. The preload function, when returned, would remove them from the collection (so I would know when they are all preloaded).
However, I think because python is waiting for the preload function, then removing the player, I am getting a set size changed during iteration error.
Hey is a simplified version of my code, I would appreciate a way to navigate this issue.
a = set([mp1, mp2 mp3])
for player in a:
preload(player)
# preload would be something like
def preload(player):
player.preloadVideo()
a.remove(player)
# This is where I believe the error gets generated.
The only solution that I can think of would be to make a copy of the set a, and then iterate through that, but I am not sure if that is the right way to do it or would even work.
You can fix it by iterating over a copy of the set. I've used list() here:
a = set(['mp1', 'mp2', 'mp3'])
# preload would be something like
def preload(player):
player.preloadVideo()
a.remove(player)
for player in list(a):
preload(player) # N.B. pass player, not a
This, however, is not a great design. For one thing the global variable, a is referenced from within the preload() function. Furthermore, the for loop iterates over all elements of the set passing each in turn to preload(), so it is not necessary to check membership of each player in a. Lastly, preload() should perform whatever is required to preload the player, but it should not be responsible for maintaining an external data structure (a).
A better design is for preload() to return a boolean indicating whether the preload was successful or not. Removal of a successfully loaded player can then be done outside of the preload function:
a = set(['mp1', 'mp2', 'mp3'])
# preload would be something like
def preload(player):
return player.preloadVideo() # assume that this returns boolean
for player in list(a):
if preload(player):
a.remove(player)
if len(a):
print "{} player(s) failed to preload: {}".format(len(a), a)
else:
print "All players successfully preloaded"
This code will only remove a player once it has been successfully preloaded.
There is no need to loop over your list 2 time! as you said, it will raise a size changed error.So instead you can use pop property of set and list to get those items which return the value and remove it from your data structure.For a list you can pass the 0 index to pop in each iteration also as a more pythoinc way you can use while instead of for when you want to remove item from your data structure :
a = [mp1, mp2 mp3]
while a:
preload(a.pop(0))
But for set the pop doesn't accept the index:
a = set([mp1, mp2 mp3])
while a:
preload(a.pop())
Example :
>>> def preload(i):
... print i
...
>>> a=[1,2,3]
>>> while a:
... preload(a.pop(0))
...
1
2
3
>>> a
[]
>>> a={1,2,3}
>>> while a:
... preload(a.pop())
...
1
2
3
>>> a
set([])
but would like return a bit in the future.
If you want to call the preload function asynchronously, I’d use the multiprocessing module:
from multiprocessing import Pool
a = set([mp1, mp2, mp3])
def preload(player):
try: # Change it to if/else depending on your type of error handling
player.preload()
except:
return player
p = Pool(5) # uses 5 threads to do the computation, change it accordingly
uninitialized = {players for players in p.map(preload, a) if player is not None}
p.close()

code runs else statement repeatedly

every time the below code runs, it goes straight through to the first else statement, then runs the next else statement four times. what is going on and how do I fix it so it calls the movement() module?
class square(object):
def __init__(self,updown,leftright,residence,name):
self.updown = updown
self.leftright = leftright
self.residence = bool
self.name = name
a1 = square(1,1,True,"a1")
a2 = square(2,1,False,"a2")
b1 = square(1,2,False,"b1")
b2 = square(2,2,False,"b2")
square_dict = {a1:"a1",a2:"a2",b1:"b1",b2:"b2"}
movement_select()
def movement_select():
response = raw_input("where would you like to move?")
if response in square_dict:
moveTo = square_dict[response]
else:
print "this runs once"
for a in square_dict:
if a.residence == True:
moveFrom = a
movement(moveFrom,moveTo)
else:
print "this runs four times"
movement_select()
Look at how you're defining residence:
self.residence = bool
So, for any square a, a.residence will be the type bool, never the boolean value True (or anything else). So this test will always fail:
if a.residence == True:
To fix it, change that first line to:
self.residence = residence
While we're at it, you rarely need == True, so you can also change the second line to:
if a.residence:
But that isn't a necessary fix, just a way of simplifying your code a bit.
Meanwhile, your squares_dict is a bit odd. I'm not sure whether it's incorrect or not, but let's take a look:
It maps from square objects to their names. That could be a useful thing to do. It means you can iterate over the dict and get all the squares—as you correctly do in your code. And if you later had a square and wanted to get its name, you could use square_dict for that. Then again, you could probably get the same benefit with just a square_list, and using the name already available as an attribute of the square objects (unless you need the same square to have different names in different contexts).
And meanwhile, a mapping in this direction can't be used for looking up squares_dict[response], because response is a name, not a square. So, you definitely need a mapping in the opposite direction, either in addition to or instead of this one.
If you scrap the square-to-name mapping and only keep the name-to-square mapping, you can still iterate over the squares; you'd just have to do for square in squares_dict.values(): instead of just for square in squares_dict:.
First problem: your dictionary appears to be backwards: you want to look up the square objects from their locations, rather than the other way around. This is why your first conditional is never true. You also might as well strip() the response to ensure that you don't have any hidden whitespace in there.
square_dict = {"a1":a1, "a2":a2, "b1":b1, "b2":b2}
# Later...
response = raw_input("where would you like to move?").strip()
# Later still...
for a in square_dict.values(): # Because we switched the dictionary around!
If you don't want to silently strip off the whitespace, I'd suggest that you at least echo their input back to them (print('"{}"'.format(response))) in the case that it's not found in your dictionary, so they (you) can be sure that at least the input was correct.
The second problem is because of how you define residence. You set the variable equal to bool, which is not what you want at all. Line five ought to read:
self.residence = residence
Finally, some other thoughts on your code! You check whether a value is True by checking if a.residence == True:. The preferred form of this comparison is the simpler version:
if a.residence:
Your methods could also be named more descriptively. Generally speaking, it's always nice to begin a function or method name with a verb, to improve readability. This is of course a question of style, but for instance, the two functions we see, movement_select and movement aren't extremely clear as to their function. It would be much easier to read if they used a standardized form, e.g. choose_move and perform_move.
self.residence = bool
should be
self.residence = residence
You don´t set residence right, this is wrong:
class square(object):
def __init__(self,updown,leftright,residence,name):
self.updown = updown
self.leftright = leftright
self.residence = bool
self.name = name
it has to be
class square(object):
def __init__(self,updown,leftright,residence,name):
self.updown = updown
self.leftright = leftright
self.residence = residence # not bool!!
self.name = name
Your response contains \n symbol, just strip() it
You should also swap places between keys and values in your dictionary

Categories

Resources