How can i sort "cardsListObj" with suit and then rank?
I've done something like "print (cardsListObj.sort (key = card.getRank))" and it didn't work, i have "None".
Actually i did this on another code that is quite the same as this (in my opinion as a newbie) and that worked. (that code is about 300 lines that's why i didn't post it, here)
class card :
def __init__ (self, rank, suit) :
self.rank = rank # cards numbers
self.suit = suit # cards characters Diamonds, Clubs, Hearts, Spades
def getRank (self):
return self.rank
def getSuit (self):
return self.suit
def main():
cards = open (input ("file? "))
cardsListObj = []
for i in cards:
cardObj = card (i.split () [0], i.split () [1])
cardsListObj.append (cardObj)
Input file is a txt file with a number and a name of cards in each line separated by space.
Thanks in advance
if you want to sort your cards, you can create a function like this:
from operator import attrgetter
def sort_cards(cards): #cards is an list containing 'Cards' objects
"""Function sorting cards"""
return sorted(cards, key=attrgetter('suit', 'rank'))
deck = [
Cards(3, 'Diamonds'),
Cards(7, 'Clubs'),
Cards(5, 'Hearts'),
Cards(8, 'Diamonds'),
Cards(4, 'Spades'),
Cards(10, 'Clubs')
]
print(sort_cards(deck))
Output (you should define the method __repr__)
['7 of Clubs', '10 of Clubs', '3 of Diamonds', '8 of Diamonds', '5 of Hearts', '4 of Spades']
NB:
In my example, __repr__ is something like that:
class Card:
[...]
def __repr__(self):
return '%d of %s' % (self.rank, self.suit)
Related
I Have this code Written with Python. The logic of the Code is to shuffle a deck of Cards, deal one single hand to the player and after Evaluate the Highest rank of the Player's hand and return an appropriate message to the player like. (Ex: Your Hand : 5K 8H 2S 3D 9S
Your Rank : Flush)
I want to use Django framework to create an API that will return an HttpResponse to mimic this (Ex: Your Hand : 5K 8H 2S 3D 9S
Your Rank : Flush) as a Json Object.
I would like to know how would about writing API and which design consideration i should have in mind.
Which part of the code should go into the view to return the appropriate endpoint exemple : the hand of the player the shuffling of the deck, and the rank of the user.
from constants import Constant
class Card(Constant):
def __init__(self, suit, val):
# initialize the card object
self.suit = suit
self.value = val
# get the rank of the card by indexing the RANKS dictionary
self.rank = self.RANKS[val]
def get_rank(self):
# return the rank of the card
if self.rank:
return self.rank
else:
raise Exception('No rank')
def get_suit(self):
# return the suit of the card
if self.suit:
return self.suit
else:
raise Exception('No suit')
def __str__(self):
# return the string representation of the card
return f'{self.get_rank()}{self.get_suit()}'
import random
from card import Card
# class to represent a deck of cards
class Deck(Card):
def __init__(self):
# initialize the deck object
self.cards = []
self.build_deck()
# Create Our Deck of 52 cards
def build_deck(self):
# loop through each suit
for suit in self.SUITS:
for rank in self.RANKS:
self.get_deck().append(Card(suit, rank))
random.shuffle(self.get_deck())
# return the deck of cards
def get_deck(self):
return self.cards
# print the deck object as a string
def __str__(self):
return " ".join([str(card) for card in self.get_deck()])
# Shuffle the deck of cards
def shuffle(self):
for i in range(len(self.get_deck()) - 1, 0, -1):
# pick a random index between 0 and i (inclusive)
if i > 0:
j = random.randint(0, i)
# swap the cards at position i and j
self.get_deck()[i], self.get_deck()[
j] = self.get_deck()[j], self.get_deck()[i]
else:
# return an empty string if the deck is empty
raise Exception('No cards to shuffle')
return True
# draw a single card from the deck (the bottom card)
def deal(self):
if self.get_deck():
# return the last card in the deck
return self.get_deck().pop()
else:
# return an empty string if the deck is empty
raise Exception('No cards to deal')
from deck import Deck
# class to represent a hand of cards
class Hand(Deck):
def __init__(self):
# initialize the hand object
self.hand = []
def get_hand(self):
# return the hand
return self.hand
def __str__(self):
if self.get_hand():
# return the string representation of the hand
return ' '.join([str(card) for card in self.get_hand()])
else:
# raise an exception if the hand is empty
raise Exception('No cards in hand')
# Make the Hand Iterable
def __iter__(self):
if not self.get_hand():
# raise an exception if the hand is empty
raise StopIteration
else:
# return an iterator for the hand
return iter(self.get_hand())
def draw_cards(self, deck, num):
# draw cards from the deck and add them to the hand
for _ in range(num):
# draw a card from the deck
cards_in_deck = deck.deal()
if cards_in_deck:
# add the card to the hand
self.get_hand().append(cards_in_deck)
else:
# raise an exception if the
raise Exception('No cards to draw')
# return the string representation of the hand
return True
class HandRank:
#staticmethod
def is_cards_in_sequence(ranks):
sorted_ranks = sorted(ranks)
for i in range(len(sorted_ranks) - 1):
# check if the next card is one rank higher than the current card
if i == 0:
# check if the first card is an ace
if sorted_ranks[i] == 'A' and sorted_ranks[i + 1] == '10':
continue
if sorted_ranks[i] != (sorted_ranks[i + 1] - 1):
return False
return True
# function to return a count of each rank in the hand
#staticmethod
def get_counts(ranks):
ranks = []
for c in ranks:
if c in ranks:
# increment the count of the rank
ranks[c] += 1
else:
# add the rank to the dictionary
ranks[c] = 1
return ranks
# get the rank of the hand
def get_hand_rank(self, hand):
suits = [card.suit for card in hand] # get the suits of the cards
ranks = [card.rank for card in hand] # get the ranks of the cards
# check for Straight flush
if self.is_cards_in_sequence(ranks) and len(set(suits)) == 1:
return Constant.STRAIGHT_FLUSH
# check for four of a kind
elif self.get_counts(ranks) == 4:
return Constant.FOUR_OF_A_KIND
# check for full house
elif self.get_counts(ranks) == 3 and self.get_counts(ranks) == 2:
return Constant.FULL_HOUSE
# check for flush
elif len(set(suits)) == 1:
return Constant.FLUSH
# check for straight
elif self.is_cards_in_sequence(ranks):
return Constant.STRAIGHT
# check for three of a kind
elif self.get_counts(ranks) == 3:
return Constant.THREE_OF_A_KIND
# check for two pair
elif len(set(self.get_counts(ranks))) == 3:
return Constant.TWO_PAIR
# check for one pair
elif len(set(self.get_counts(ranks))) == 4:
return Constant.ONE_PAIR
#
else:
return Constant.HIGH_CARD
Exemple of the output:
Your Hand : 5K 8H 2S 3D 9S
Your Rank : Flush
How can I print out the list of "foods"?
All I can print was something like memory location.
class Fridge:
isOpened = False
foods = []
def open(self):
self.isOpened = True
print "Fridge open"
def put(self, thing):
if self.isOpened:
self.foods.append(thing)
print 'Food in'
else:
print 'Cannot do that'
def close(self):
self.isOpened = False
print 'Fridge closed.'
def __repr__(self):
return "%s" % (self.foods[0])
class Food:
pass
The way you defined repr(), it will only produce the first item in the list foods (as indicated by foods[0]). Furthermore, if the list foods is empty, calling repr() will result in an IndexError.
One way to print the list would be:
def __repr__(self):
return str([food for food in self.foods])
If you are not familiar with the syntax, please check out List Comprehensions.
Here's an example use case of your class:
>>> f = Fridge()
>>> f.open()
Fridge open
>>> f.put("Fruit")
Food in
>>> f.put("Vegetables")
Food in
>>> f.put("Beer")
Food in
>>> print f
['Fruit', 'Vegetables', 'Beer']
I'm trying to write a blackjack game for an online class I'm taking. I know that I'll have to introduce objects at some point, but right now I'm just trying to write the very basic code for operations within the object. I also have only chemistry as my background, absolutely zero computer science background.
What I'm trying to do is to pull the value text value from the first entry in the tuple, then assign it a numerical value so I can start to compare scores, once I get to the point of creating players. However, I get this error, and I don't fully know what it means...the code is also below. Still a noob here, just trying to learn!
27 def value_of_card():
--->28 x = deal[0]
29
30 print value_of_card()
TypeError: 'function' object has no attribute 'getitem'
import random
suits = ['spades', 'hearts', 'clubs', 'diamonds']
ranks = ['ace', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'Jack', 'Queen', 'King']
def deal():
deck = []
for suit in suits:
for rank in ranks:
deck.append((rank, suit))
random.shuffle(deck)
print deck
deal = deck[0]
print deal
for card in deck:
if deal in deck:
deck.remove(deal)
return deck
print deck
print deal[0]
print deal()
def value_of_card(deal):
return deal[0]
print value_of_card(deal)
Some issues:
You pass the function reference to value_of_card, instead of passing the value returned by that function.
Your deal function does not return a card, but the deck of remaining cards. You should not return deck, but return deal
The search (with for loop) for the card that you selected is not necessary: you took it from index 0, so just remove the card at index 0 (with pop(0))
Here is the corrected script:
import random
suits = ['spades', 'hearts', 'clubs', 'diamonds']
ranks = ['ace', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'Jack', 'Queen', 'King']
def deal():
deck = []
for suit in suits:
for rank in ranks:
deck.append((rank, suit))
random.shuffle(deck)
# you don't need to search for the card. It is at index 0
# grab, remove and return the card (not the deck) in one go:
return deck.pop(0)
card = deal() # remember the dealt card
print card
def value_of_card(deal):
return deal[0]
print value_of_card(card) # pass the remembered card
And here is how it could look if you made it more OOP:
import random
class Card:
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __repr__(self):
return self.rank + ' of ' + self.suit
class Deck:
def __init__(self):
suits = ['spades', 'hearts', 'clubs', 'diamonds']
ranks = ['ace', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'Jack', 'Queen', 'King']
self.__deck = [Card(rank, suit) for suit in suits for rank in ranks]
def shuffle(self):
random.shuffle(self.__deck)
def deal(self):
return self.__deck.pop()
# Create a deck of 52 cards
deck = Deck()
# Shuffle the deck
deck.shuffle()
# Pull a card from the deck
card = deck.deal()
# Show the card
print(card) # this calls the __repr__ method
# ..or only the rank:
print(card.rank)
You forgot to call your deal function in value_of_card function.
You should also change your functions names to be distinct, because now you have too many objects called deal which is really misleading and makes your code hard to read.
I'm trying to use python properties and inheritance and something isn't behaving intuitively. I want to be able to utilize property getters and setters of the inherited class to avoid having to repeat code for repeated behavior.
In order to boil down my problem I created the following example. I have a Car with behavior for counting passengers and populating the car's seats when given some number of passengers (using the occupants property getter and setter). For a van with 3 rows of seats I should only have to define the behavior of the 3rd seat and defer to the inherited Car class for the first 2 rows...
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
#property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
#occupants.setter
def occupants(self, val):
#Start with an empty car
self._contents['row1'] = 0
self._contents['row2'] = 0
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
#property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
#occupants.setter
def occupants(self, val):
#Start with an empty van (Car class handles row1 and row2)
self._contents['row3'] = 0
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
#This causes an AttributeError
super(Van, self).occupants = 4
else:
#This causes an AttributeError
super(Van, self).occupants = val
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
The output I get is as follows:
Van has 3 people
Seating 6 people in the van...
Traceback (most recent call last):
File "C:/scratch.py", line 74, in <module>
van.occupants = 6
File "C:/scratch.py", line 65, in occupants
super(Van, self).occupants = 4
AttributeError: 'super' object has no attribute 'occupants'
Process finished with exit code 1
What's especially interesting to me is that the superclass's getter is working fine, but when I try to use the setter I get the attribute error. Am I using super() incorrectly? Is there a better way to do this?
My actual application involves reading/writing between a text file format and a dictionary-like data structure. Some of the stuff in the text file is parsed out by my base class and some other special parameters are handled by the subclass. In the subclass setter I want to start by letting the base class parse whatever it needs from the text file (populating the data structure), then let the subclass parse out additional values for storage in the inherited data structure.
Some research lead me to this and eventually Issue 505028 where it is declared a "feature" instead of a bug. So with that out of the way, is there a way to make the above logic work using properties and inheritance? Do I have to use Car.occupants.fset(self, 4) or something? I may answer myself in a minute, but I'm going to go ahead and post this to share it with folks. SOrry if it's a duplicate.
edit:
Fixed some additional bugs like emptying all seats before setting occupants and the numeric logic in the Van occupants setter was wrong and incomplete (only became evident once the property error was fixed).
As you've noted, Guido van Rossum says,
... the semantics of super ... only applies to
code attributes, not to data attributes.
So the workaround is to call a code attribute, not a data attribute. That means, in this case, that you need to keep a reference to the setter method; let's call it set_occupants. So instead of
#occupants.setter
def occupants(self, val):
use
def set_occupants(self, val):
...
occupants = property(get_occupants, set_occupants)
and instead of super(...).occupants = 4, you call the super's method:
super(Van, self).set_occupants(4)
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
def get_occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
def set_occupants(self, val):
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
occupants = property(get_occupants, set_occupants)
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
def get_occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
def set_occupants(self, val):
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
super(Van, self).set_occupants(4)
occupants = property(get_occupants, set_occupants)
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
yields
Van has 3 people
Seating 6 people in the van...
Van has 6 people
To continue using the #property decorator and yet still be able to call the setter from super, and without having to manually add lots of extra attributes by hand, you could use a metaclass to do the work for you. A class decorator would also be possible, but the advantage of a metaclass is that you need only define it once as the metaclass of Car, and then the metaclass and its behaviour is inherited by all subclasses of Car, whereas, a class decorator would have to be applied to each subclass manually.
class MetaCar(type):
def __init__(cls, name, bases, clsdict):
super(MetaCar, cls).__init__(name, bases, clsdict)
for name, val in clsdict.items():
if isinstance(val, property):
setattr(cls, 'get_{}'.format(name), val.fget)
setattr(cls, 'set_{}'.format(name), val.fset)
setattr(cls, 'del_{}'.format(name), val.fdel)
class Car(object):
__metaclass__ = MetaCar
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
#property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
#occupants.setter
def occupants(self, val):
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
#property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
#occupants.setter
def occupants(self, val):
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
super(Van, self).set_occupants(4)
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
Looking around the web I found that SomeClass.property.fset(self, value) can be used to call the setter of a property. In this case SomeClass is Car (the super-class of Van) and self is the current Van instance, which the car occupants setter operates on to populate the first 2 rows of the van (just like for a car).
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
#property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
#occupants.setter
def occupants(self, val):
#Start with an empty car
self._contents['row1'] = 0
self._contents['row2'] = 0
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
#property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
#occupants.setter
def occupants(self, val):
#Start with an empty van (first 2 rows handled by Car class)
self._contents['row3'] = 0
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
Car.occupants.fset(self, 4)
else:
#Load the first 2 rows in the same manner as for a car
Car.occupants.fset(self, val)
if __name__ == '__main__':
van1 = Van()
van2 = Van()
print "Van has {0} people".format(van1.occupants)
print "Seating 6 people in van1"
van1.occupants = 6
print "Seating 2 people in van2"
van2.occupants = 2
print "van1 has {0} people".format(van1.occupants)
print "van2 has {0} people".format(van2.occupants)
The output is:
Van has 3 people
Seating 6 people in van1
Seating 2 people in van2
van1 has 6 people
van2 has 2 people
Props also to unutbu for lots of work on this and demonstrating that this could also be solved with the property function or a metaclass. I'm not yet sure which is more elegant since each method has pro's and con's.
If answering my own question is bad form in this instance, call me out and I'll gladly do whatever is necessary to follow the community protocol.
First off I am getting this error
File "E:\New folder (7)\maingame.py", line 64, in play print self.introduction AttributeError: 'game' object has no attribute 'introduction'
I am not to sure as to what it means because I am pulling the self.introduction from the previous class..
I am also getting an
File "E:\New folder (7)\maingame.py", line 96, in <module>
game.play()
TypeError: play() takes exactly 2 arguments (1 given)
error, but can't for the life of me find what argument it is looking for, I simply want it to work.
from random import random
class place(object):
def __init__(self, title, description, events):
self.title = title
self.description = description
self.events = events
class event(object):
def __init__(self, probability, message, healthChange):
self.probability = probability
self.message = message
self.healthChange = healthChange
def process(self):
if random() < self.probability:
print self.message
return self.healthChange
return 0
class textadventure():
def __init__(self):
super(textadventure, self).__init__()
self.introduction = """
Welcome player, you are a lone traveler in space whom has set out to find glories beyond measure.
Unfortunately for you the dread pirate Roberts has attacked. You must defeat him.
"""
commandDeck = place('Command Deck', "You are now in the command center, here you can drive the ship and fire its weapons.",(
event(0.7, "The pirate ship fires at you! You take damage to your engines!", -10),
event(0.2, "One of the pirates manages to beam onto your ship! He shoots you before beaming away!",0),
))
engineRoom = place('Engine Room', "You are now in the main engine room here you can repair damage to the ship",(
event(0.7, "The pirate ship fires at you! You take damage to your engines!", -10),
))
restQuarters = place('Resting Quarters', "Here you can take a rest and heal your self",(
event(1.0, 'You are able to patch up your wounds and get back to the battle',0),
event(0.5, "The pirate ship fires at you! You take damage to your engines!", -10),
))
commandDeck.transitions = (engineRoom, restQuarters),
engineRoom.transitions = (commandDeck, restQuarters),
restQuarters.transitions = (commandDeck, engineRoom),
self.location = commandDeck
pirateHp = 50
class game(object, textadventure):
def __init__(self):
super(game, self).__init__()
self.health = 100
def location(self):
if self.location == commandDeck:
choice = raw_input('would you like to fire on the enemy ship?')
if choice == 'yes':
print 'You have hit the pirates!'
pirateHp -= 10
else: choice == 'no'
elif self.location == engineRoom:
choice = raw_input('Would you like to repair the engines?')
if choice == "yes":
event(1, "You repair what you can of the engines.", 10)
def __init__(self):
self.health = 100
def play(self, textadventure):
print textadventure.introduction
while True:
print (self.location.description)
for event in self.location.events:
self.health += event.process()
if self.health <= 0:
print ("Your ship has been destroyed!")
pause
exit(1)
print ('Your ships health is at %d percent' % self.health)
self._transition()
def _transition(self):
transitions = self.location.transitions
print ('you can go to: ')
for (index, transition) in enumerate(transitions):
print (index + 1, transition.title)
choice = int(raw_input('Choose one '))
if choice == 0:
exit(0)
else:
self.location = transitions[choice - 1]
def pirateShip(Object):
if pirateHp == 0:
print "You have defeated the pirates! Congradualations!"
pause
exit(1)
game = game()
game.play(game)
'game' object has no attribute 'introduction'
You should call the init of your super class when initializing game. In your current code, textadventure.init is never called which is why introduction is never added to textadventure.
Game should also not inherit from object (it does that through textadventure).
class game(textadventure):
def __init__(self):
super(game, self).__init__()
self.health = 100
def play(self):
print self.introduction
Should do the trick.
TypeError: play() takes exactly 2 arguments (1 given)
You never use your textadventure argument in play. Removing this should get things working.