How to create string representations of objects in a list? - python

I'm trying to create a game of Kings Cup (drinking game that involves cards) with Python. As it stands at the moment, when I print the deck and participants lists, it appears that the console is print out memory addresses. I would appreciate any help, pointers, tips, suggestions. Please and thank you.
import random
class Participant:
def __init__(self, name, gender):
self.name = name
self.gender = gender
self.cards = []
def drawCard(self):
self.cards += deck.pop()
def showCards(self):
print(self.name)
print(self.cards)
class Card:
def __init__(self, value, suite):
self.value = value
self.suite = suite
def getValue(self):
return self.value
def getSuite(self):
return self.suite
def __str__(self):
return ("{0} of {1}".format(self.value, self.suite))
deck = []
participants = []
def shuffleDeck():
for suite in ["Clubs", "Diamonds", "Hearts", "Spades"]:
for num in ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]:
deck.append(Card(num, suite))
random.shuffle(deck)
return deck
players = input("Enter the number of players: ")
numPlayers = int(players)
if numPlayers < 3:
print(players)
print("Not enough players.")
elif numPlayers > 12:
print(players)
print("That's too many players.")
else:
for player in range(numPlayers):
player += 1
name = input("Player %s's Name: " %player)
gender = input("Player %s's Gender: " %player)
participants.append(Participant(name, gender))
deck = shuffleDeck()
print(deck)
print(participants)

Define a representation for each instance. For example:
class Card:
def __init__(self, value, suite):
self.value = value
self.suite = suite
def getValue(self):
return self.value
def getSuite(self):
return self.suite
def __str__(self):
return ("{0} of {1}".format(self.value, self.suite))
def __repr__(self):
return ("{0} of {1}".format(self.value, self.suite))
Then
>> a = [Card(3,'Diamonds'),Card(2,'Spades')]
>> print(a)
[3 of Diamonds, 2 of Spades]
That said, see #abarnerts comments about formatting repr

Python stores information in memory with pointers. When you create a variable, you say: Hey python, remember that this variable points to this piece of memory.
When you print a string or a number, python will auto format it for you, but when more complex objects are involved, it doesn’t know what to do.
To inform python what to do, you need to create the magic function __str__ (2 underscores on each sides) which you already did.
The problem is, when you print a list, you don’t print each individual objects, you print what the list represents of each objects. It’s more useful like that, because you’re not storing simple objects like strings or ints, but complex and defined structures.
To tell python what to represent, you need to create the magic function __repr__. I strongly suggest that you do not copy the __str__ function. It would be preferred to print something like Card(*value*, *suite*). In other words: your init function.
Gift code:
def __repr__(self):
return '{}({}, {})'.format(self.__name__, self.value, self.suite)

Related

Python: How to compare elements within an array of cards

I am working on a texas hold-em game in python, and am looking to traverse an array containing a complete hand of 7 cards (2 in the hole + 5 on the board). The array contains elements of class Cards, where the Card class constructor is
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
So, I have a "hand" array within a "Player" class of 7 random cards, where the suit is one of 4 strings (spade, club, heart, diamond) and the value is one of 9 numbers (2-10) or 4 strings (jack-ace). I want to traverse the array to check if the list contains any of the hands in poker, but I'm failing to figure out how I can "extract" the suit/value of the cards from my array. I've started a method within my "Player" class to check for a suit, here that suit is a spade.
class Player:
def __init__(self, name):
self.name = name
self.pocket = []
self.hand = []
def spadeChecker(self):
card = Card("Blank", 0)
for i in self.hand:
card = self.hand[i]
if(card.suit == "Spade"):
print("Hi! you have a spade!")
else:
pass
When running the program from my terminal I receive a TypeError message:
in spadeChecker card = self.hand[i] TypeError: list indices must be integers or slices, not Card
I know my method is pretty bad but I'm very new to this and just can't figure out how to get it to work. Any advice?
Thanks
Here is the rewritten method, including an example with a 3 card hand.
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
class Player:
def __init__(self, name):
self.name = name
self.pocket = []
self.hand = [Card("Heart", 10), Card("Spade", 10), Card("Diamond", 10)]
def spadeChecker(self):
for card in self.hand:
if(card.suit == "Spade"):
print("Hi! you have a spade!")
#return True
else:
#return False
pass
p = Player("Bob")
p.spadeChecker()
Output is:
Hi! you have a spade!
In your code, your iterator is i and your iterable is self.hand, which is a list of Card objects. Thus, i will be a Card object each time the loop iterates. If you want to be able to get the index of the Card object as well as the Card object itself, I recommend using the enumerate() function. However, since you only reference the Card objects, you can just get the suit attribute directly.
class Player:
def __init__(self, name):
self.name = name
self.pocket = []
self.hand = []
def spadeChecker(self):
card = Card("Blank", 0)
for card_obj in self.hand:
if(card_obj.suit == "Spade"):
print("Hi! you have a spade!")
else:
pass
There's a Python library for generating, comparing & evaluating poker hands called tilted which can be found here: https://github.com/MaxAtkinson/tilted
I've used it myself before and it's really easy. This may allow you to avoid having to implement it yourself.

How do I make my (derived?) python instance attributes "self-update" based on alterations to other same-instance attributes?

I know that I have a misunderstanding of how Python attributes work because I'm here writing this problem, but I don't know exactly what I'm misunderstanding. I'm trying to get
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
to attain their values based on self.hand, which I pass to __init__ upon instantiation. Throughout the game I am altering .hand, and I want .card & .card_val to change every time I change .hand, instead of having to tell it to do that elsewhere (outside of the attribute definitions). I know there is a way to do this, or at least I think there is, and by that I mean simply by defining their values as inherited based on whatever .hand is at any given time, without calling an internal or external function.
In the posted code, I have altered it to work as the game instructions require, using...
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
...but that's what I want to change. I want what that function is doing to be executed more concisely inside of the attribute definitions upon handling of the instance. Basically my question is why can't these two attributes get their values directly from the first attribute that I define via "hand" passed to the init?
Any help would be appreciated, and more importantly, I think more than just solutions, it would help me even more to understand what I am misunderstanding about how attributes, instances, and instantiation and all that works so that I know where my thinking is wrong. Thanks!
import random
from random import shuffle
from collections import deque
class Deck():
def __init__(self):
self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
self.suites = ["Heart", "Diamond", "Spade", "Club"]
self.cards = []
def create_cards(self):
for suite in self.suites:
for key in self.ranks.keys():
self.cards.append(key + " " + suite)
def shuffle(self):
random.shuffle(deck.cards)
deck = Deck()
deck.create_cards()
deck.shuffle()
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
def war(bool, p1, p2):
if bool == True:
for player in [p1, p2]:
player.card_number = 4
else:
for player in [p1, p2]:
player.card_number = 0
p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))
p2.name = "The Computer"
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
def cant_war_lose(winner, loser):
print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))
def battle_win(winner, loser):
print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))
def play_cards(p1, p2):
war(False, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-1)
elif p1.card_val == p2.card_val:
if len(p1.hand) < 5 or len(p2.hand) < 5:
if len(p1.hand) > len(p2.hand):
cant_war_lose(p1.name, p2.name)
else:
cant_war_lose(p2.name, p1.name)
return 0
else:
input("War is inititated! Press Enter to continue!")
print("---------------------------")
war(True, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
for i in range(0,5):
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-5)
elif p1.card_val < p2.card_val:
for i in range(0,5):
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-5)
else:
p1.hand.rotate(-1)
p2.hand.rotate(-1)
elif p1.card_val < p2.card_val:
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-1)
if len(p1.hand) != 0 and len(p2.hand) != 0:
input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
print("---------------------------")
else:
if len(p1.hand) > len(p2.hand):
battle_win(p1.name, p2.name)
else:
battle_win(p2.name, p1.name)
return 0
def game_run():
run = 1
p1.name = input("Player 1's name? ")
print("---------------------------")
while run == 1:
if play_cards(p1, p2) == 0:
run = 0
game_run()
You can use the property decorator to create a calculated property
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
#property
def hand(self):
return self._hand
#hand.setter
def hand(self, value):
self._hand = value
self.card = self._hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
What you misunderstand is variables, not instances. For instance, the attribute card is a scalar variable attached to the instance. Assigning to it with
self.card = <blah>
does not bind it to blah for constant recomputation. This is a value assignment, not a memory mapping. If you want that long-term binding, you must either write the maintenance routine yourself -- which you've already done, in a way, with the consistent recomputation -- or you must assign a mutable reference to self.card, so that card refers to teh same object as the expression you created.
Given that you are consistently rotating and altering the hand, this is not feasible in your design. Instead, simply write an access routine, perhaps get_next_card(hand), which will rotate the hand, extract the card, and return the desired rank and suit.
If you plan to program more card games, you will also find it handy to define a class card and class hand, with appropriate support routines. Maintain the card as a pair of integers; convert to strings only for printing.
Does that get you moving?
For anyone who wanted to compare a before and after of the problem & final solution, below is the working code for my specific issue. All I had to do was convert self.card and self.card_val to a calculated property. By passing in hand, and subsequently handling only hand, self.card & self.card_val are calculated, since every time I handle the instance of the class (by handling hand), these "method attributes" are being called and altered. Thanks for the input, guys!
class Player():
def __init__(self, hand):
self.name = "name"
self.card_number = 1
self.hand = hand
#property
def card(self):
return self.hand[self.card_number].split()
#property
def card_val(self):
return deck.ranks.get(self.card[0])

Deck card class in python

I am working on creating a class for the first time, and I am thinking that I have done every thing to get it run, but I still get bunch of issues which is 'list' object has no attribute 'shffule' so the problem here is it will not shuffle the cards, and it will not tell the remaining cards, can any one tell me what am I doing wrong? Thanks in advance
import random
class card_deck:
def __init__(self, rank, suite, card):
self.rank= rank
self.suite = suite
def ranks(self):
return self.rank
def suites(self):
return self.suite
def cards(self,card):
suit_name= ['The suit of Spades','The suit of Hearts', 'The suit of Diamonds','Clubs']
rank_name=['Ace','2','3','4','5','6','7','8','9','10','Jack','Queen','King']
def value(self):
if self.rank == 'Ace':
return 1
elif self.rank == 'Jack':
return 11
elif self.rank == 'Queen':
return 12
elif self.rank == 'King':
return 13
def shffule(self):
random.shuffle(self.cards)
def remove(self,card):
self.cards.remove(card)
def cardremaining(self):
self.suite-self.rank
def main():
try:
deck=[]
for i in ['Spades','Hearts', ' Diamonds','Clubs']:
for c in ['Ace','2','3','4','5','6','7','8','9','10','Jack','Queen','King']:
deck.append((c, i))
deck.shffule
hand = []
user =eval(input('Enter a number of cards: 1-7 '))
print()
while user <1 or user >7:
print ("Only a number between 1-7:")
return main()
for i in range(user):
hand.append(deck[i])
print (hand)
except ValueError:
print("Only numbers")
main()
Apart from your code containing many small errors; I will try to answer your main problems.
If you are going to use shffule[sic] method of card_deck class, then you first need to create an instance of that class(whereas you tried to call that method with a list). Like this:
deck = card_deck(some_rank,some_suit,some_card)
deck.shffule() #Now you can call the method
Now, since you made it a class instance, you cannot get items from it like hand.append(deck[i])
Unless you defined the method __getitem__ in your class definition, like this:
#this will be in your class definition
def __getitem__(self,i):
return self.card_list[i] #Of course you have to define a list of cards in your class too.
In my opinion, you should spend a little more time trying to understand how is a class defined, how does methods work and how to access members of a class. After that you will be doing much better here
You are never actually creating an instance of the card_deck class. The expression
deck=[]
creates a variable named deck referring to an empty list.
In python, [a, b, c,...] is the syntax for creating list literals.
from collections import namedtuple
Card = namedtuple('Card', 'sign, value') # no need to write class to represent card
SIGNS = ['Hearts', 'Diamonds', 'Spades', 'Clubs']
class Deck:
def __init__(self):
self.cards = [Card(sign, value) for sign in SIGNS for value in range(2,
11) +
'J Q K A'.split()]
def __repr__(self):
return str([str(card) for card in self.cards])
def __len__(self):
return len(self.cards)
def __getitem__(self, item):
return self.cards[item]
def __setitem__(self, key, value):
self.cards[key] = value
deck = Deck()
print deck[11] # indexing works, prints Card(sign='Hearts', value='K')
print len(deck) # prints 52
print deck[13:16] # slicing works
import random
random.shuffle(deck) # shuffle works using no extra code

Accessing a class/method from another program in Python

I have a program (blackjack.py) and it accesses another program's (cards.py and games.py) within its code. Most of this is from a book, so I'm having trouble understanding how it works.
Heres the code for cards.py:
class Card(object):
""" A playing card. """
RANKS = ["A", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "J", "Q", "K"]
SUITS = ["c", "d", "h", "s"]
def __init__(self, rank, suit, face_up = True):
self.rank = rank
self.suit = suit
self.is_face_up = face_up
def __str__(self):
if self.is_face_up:
rep = self.rank + self.suit
else:
rep = "XX"
return rep
def flip(self):
self.is_face_up = not self.is_face_up
class Hand(object):
""" A Hand of playing cards. """
def __init__(self):
self.cards = []
def __str__(self):
if self.cards:
rep = ""
for card in self.cards:
rep += str(card) + "\t"
else:
rep = "<empty>"
return rep
def clear(self):
self.cards = []
def add(self, card):
self.cards.append(card)
def give(self, card, other_hand):
self.cards.remove(card)
other_hand.add(card)
class Deck(Hand):
""" A deck of playing cards. """
def populate(self):
for suit in Card.SUITS:
for rank in Card.RANKS:
self.add(Card(rank, suit))
def shuffle(self):
import random
random.shuffle(self.cards)
def deal(self, hands, per_hand = 1):
for round in range(per_hand):
for hand in hands:
if self.cards:
top_card = self.cards[0]
self.give(top_card, hand)
else:
print "Can't continue deal. Out of cards!"
if __name__ == "__main__":
print "This is a module with classes for playing cards."
raw_input("\n\nPress the enter key to exit.")
I'm writing an error check for the blackjack.py and I need to gather the number of cards that have been used so far. I think I can do that by accessing the number of values in cards[]. The problem is, I am not sure on how to do that.
This is all in theory though. I will include the `blackjack.py' code as well, so you all can see what I am trying to do, and help me determine if my logic is flawed.
blackjack.py code
Any and all input is appreciated.
While I'm not entirely clear on your intended structure here, you have a couple of options.
First, in order to use any functions or classes from cards.py in your blackjack.py module, you can use the import statement to import them. There are two styles of doing this:
# blackjack.py
import cards
Would give you access to everything in the cards module, and you'd call each function/class by prefacing it with cards.<name>. So, if you wanted to initialize an instance of the Deck class,
# blackjack.py
import cards
mydeck = cards.Deck()
The other way is the from <X> import <Y>, which gives you access to the functions and classes without having to add the prefix. Example:
# blackjack.py
from cards import Deck # or, to import everything, "from cards import *"
mydeck = Deck()
Either of these methods would give you an instance of the cards.Deck class.
Option 0
You can already tell this within your Deck class, since it subclasses from Hand, so every time you give a card it's taking out out of the Deck's cards attribute. Thus, the number of cards given out would simply be:
class Deck(Hand):
# ...
def number_cards_used(self):
return 52 - len(self.cards)
Alternatively, if you can't edit cards.py, you can simply get the number of cards left from your given Deck by:
# blackjack.py
def get_number_cards_used_from_deck(deck):
return 52 - len(deck.cards)
In use:
# blackjack.py
import cards
mydeck = cards.Deck()
# ...
# Do other operations with deck
# ...
cards_used = get_number_cards_used_from_deck(mydeck)
Option 1
If you can isolate all of and only those hands being played simultaneously, you can implement a method card_count:
class Hand(object):
# ...
# other code
# ....
def card_count(self):
return len(self.cards)
Then, if you have a list of all the hands, for example, you could do something like:
sum(map(lambda h: h.card_count(), list_of_hands))
Option 2
Within your Deck class, since it subclasses Hand, you could simply keep another running list of the cards that have been given out that would often be refreshed. This would look like:
class Deck(Hand):
# ...
# other code
# ...
def __init__(self):
self.populate()
self.used_cards = []
def give(self, card, other_hand):
self.used_cards.append(card)
super(Deck, self).give(card, other_hand)
def number_cards_used(self):
return len(self.used_cards)
There are other methods, of course.

How could I add restrictions or rules to a list in Python?

I'm doing a solitaire program, and it's meant to run in the Python shell instead of a GUI. But I'm not quite so sure how to program the cards so that a Red Suit can be appended onto a Black Suit and vice versa. Also, the number of the card that is to be appended has to be lower than card before it.
I suppose the program used to add restrictions to the row stacks could be similar to the program used to add restrictions to the suit stacks. If it's different, could you also give me some help on this? Because I've nearly finished the project, but I've just got to add rules.
I've tried to figure out how I could do it, but it seems quite complicated.
def check(card1,card2):
valid = True
cardSuit = Card1[1]
cardVal = card1[0]
if cardSuit2 == cardSuit[1]:
valid = False
elif value(CardVal1) > value(cardVal2):
valid = False
elif valid == False:
# print Error Message
return valid
# Function to call above function
def call():
ok = check(card1,card2)
if ok = True:
# proceed with code
def value(str):
if val is (2,3,4,5,6,7,8,9,10):
denon = int(val)
elif val = 'J':
denon = 11
elif val = 'Q':
denon = 12
elif val = 'K':
denon = 13
return denon
Thanks.
Here is one possiblity. It's not perfectly clean data-wise, but might give you an idea. However I suppose you better understand the example really well, before you copy. As mentioned it is quite a difference to your initial idea ;)
CARD_VALUES=["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
SUIT_COLOR={"clubs": "black", "spades":"black", "hearts":"red", "diamonds":"red"}
class Card:
def __init__(self, value, suit):
if value not in CARD_VALUES:
raise Exception("Invalid card value {}".format(value))
if suit not in SUIT_COLOR:
raise Exception("Invalid suit {}".format(suit))
self.value = value
self.suit = suit
def suit_color(self):
return SUIT_COLOR[self.suit]
def card_value(self):
return CARD_VALUES.index(self.value)
def __repr__(self):
return "({} of {})".format(self.value, self.suit)
class Row:
def __init__(self):
self.cards=[]
def valid_add_card(self, card):
if not self.cards:
return True # true for empty deck
last_card=self.cards[-1]
return last_card.card_value()==card.card_value()+1 and last_card.suit_color()!=card.suit_color()
def add_card(self, card):
if self.valid_add_card(card):
self.cards.append(card)
else:
raise Exception("Cannot add card {} to {}".format(card, self.cards[-1]))
def __repr__(self):
return " - ".join(str(c) for c in self.cards)
r=Row()
r.add_card(Card("J", "spades"))
r.add_card(Card("10", "hearts"))
r.add_card(Card("9", "clubs"))
print(r)
print("Last card won't work:")
r.add_card(Card("8", "spades"))
Subclass list and implement the functionality that you want.http://www.cafepy.com/article/python_attributes_and_methods/ch03s02.html
If you subclass Python List it comes with FREE built in functionality that would work perfect for solitare, append and pop you could implement your desired card checking then call the original List method.
Additionally I would make a Card object. It might be easier to keep everything organized this way. It could keep track of it's color, suite, human display(if it's a face card)

Categories