|
Spring Semester 2007
|
Nine: Object-Oriented Design: The Blackjack Game.
Consider the following code:
class one(object):
def __init__(self, name):
self.name = name
def hurt(self):
print self.name + ": ouch!..."
def shoot(self, other):
print self.name + ": I am shooting " + other.name
other.hurt()
def test():
a = one("Arthur")
b = one("Buster")
a.shoot(b)
b.shoot(a)
b.shoot(a)
|
It produces the following output:
>>> test()
Arthur: I am shooting Buster
Buster: ouch!...
Buster: I am shooting Arthur
Arthur: ouch!...
Buster: I am shooting Arthur
Arthur: ouch!...
>>>
|
This is just like the Alien Blaster program.
|
The difference is that we use one and the same type of objects.
|
Now would be a good time to set the goal for this chapter.
|
Let's play the game, see how it goes.
|
Copying the blackjack.py file and running it won't be enough.
|
Why? What's missing?
|
It needs two modules: games.py , and
cards.py
|
So, copy these first, then run the program.
|
This is important.
|
Yes, but when you finally run it, how does it go?
|
Take a look.
|
Hey, that's cool!
|
Welcome to Blackjack!
How many players? (1 - 7): 2
Enter player name: Larry
Enter player name: Rick
Larry: 8d 4d (12)
Rick: 9d Qh (19)
Dealer: XX Qs
Larry, do you want a hit? (Y/N): y
Larry: 8d 4d 6c (18)
Larry, do you want a hit? (Y/N): n
Rick, do you want a hit? (Y/N): n
Dealer: Ac Qs (21)
Larry loses.
Rick loses.
Do you want to play again?: y
Larry: 3h 2s (5)
Rick: Jd Ad (21)
Dealer: XX 7c
Larry, do you want a hit? (Y/N): y
Larry: 3h 2s Ah (16)
Larry, do you want a hit? (Y/N): y
Larry: 3h 2s Ah 10c (16)
Larry, do you want a hit? (Y/N): y
Larry: 3h 2s Ah 10c Kh (26)
Larry busts.
Larry loses.
Rick, do you want a hit? (Y/N): n
Dealer: 2c 7c (9)
Dealer: 2c 7c Jh (19)
Rick wins.
Do you want to play again?: n
Press the enter key to exit.
Here are pictures of Larry and Rick.
|
No wonder, then.
|
Wouldn't it be better if we could play with real cards?
|
Oh, I like those. Of course it would...
|
So that's what we will do when we do web programming.
|
Great. Let's get back to our current chapter.
|
We have already covered the alien blaster.
|
We need to cover games and cards before we talk about blackjack.
|
Good. That's the big picture.
|
On a smaller scale our next step is the playing cards program.
|
What does it do?
|
Here's how it runs:
|
Printing a Card object:
Ac
Printing the rest of the objects individually:
2c
3c
4c
5c
Printing my hand before I add any cards:
Printing my hand after adding 5 cards:
Ac 2c 3c 4c 5c
Gave the first two cards from my hand to your hand.
Your hand:
Ac 2c
My hand:
3c 4c 5c
My hand after clearing it:
Press the enter key to exit.
It's a test program, basically.
|
That's right. Here's the main part of the program.
|
# main
card1 = Card(rank = "A", suit = "c")
print "Printing a Card object:"
print card1
card2 = Card(rank = "2", suit = "c")
card3 = Card(rank = "3", suit = "c")
card4 = Card(rank = "4", suit = "c")
card5 = Card(rank = "5", suit = "c")
print "\nPrinting the rest of the objects individually:"
print card2
print card3
print card4
print card5
my_hand = Hand()
print "\nPrinting my hand before I add any cards:"
print my_hand
my_hand.add(card1)
my_hand.add(card2)
my_hand.add(card3)
my_hand.add(card4)
my_hand.add(card5)
print "\nPrinting my hand after adding 5 cards:"
print my_hand
your_hand = Hand()
my_hand.give(card1, your_hand)
my_hand.give(card2, your_hand)
print "\nGave the first two cards from my hand to your hand."
print "Your hand:"
print your_hand
print "My hand:"
print my_hand
my_hand.clear()
print "\nMy hand after clearing it:"
print my_hand
raw_input("\n\nPress the enter key to exit.")
Can you go over it, briefly?
|
I sure can. It starts by creating a card object.
|
It then creates a few more cards, prints them, assembles them in a hand.
|
I suppose you mean Card , Hand instead.
|
As a matter of fact: yes.
|
Whhat do objects of type Card look like?
|
Extremely simple structure.
|
I'd say they look like 2D points. They're just pairs.
|
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):
self.rank = rank
self.suit = suit
def __str__(self):
rep = self.rank + self.suit
return rep
What is a Hand ?
|
Nice little abstraction; in reality, a glorified array.
|
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) + " "
else:
rep = ""
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)
Oh, boy!
|
Why are you saying that?
|
I just realized there are two more versions of this program.
|
Well, let's see them.
|
OK, here's what the second one does, first.
|
Looks interesting...
|
Created a new deck.
Deck:
Populated the deck.
Deck:
Ac 2c 3c 4c 5c 6c 7c 8c 9c 10c Jc Qc Kc Ad 2d 3d 4d 5d 6d
7d 8d 9d 10d Jd Qd Kd Ah 2h 3h 4h 5h 6h 7h 8h 9h 10h Jh Qh
Kh As 2s 3s 4s 5s 6s 7s 8s 9s 10s Js Qs Ks
Shuffled the deck.
Deck:
9h Kd Qc 9d As Js 4c 10d 5s 8h 6d Jc 3s 2s Qd Qs 10s 5c 6h
5d 7c Jd 10c Qh 4s 4d 2d 7h Ah 6s 3d 2h 10h Ad Ks Kc 7s 3c
4h 9c 2c 8d 8s Ac 7d 8c Kh 9s Jh 6c 3h 5h
Dealt 5 cards to my hand and your hand.
My hand:
9h Qc As 4c 5s
Your hand:
Kd 9d Js 10d 8h
Deck:
6d Jc 3s 2s Qd Qs 10s 5c 6h 5d 7c Jd 10c Qh 4s 4d 2d 7h Ah
6s 3d 2h 10h Ad Ks Kc 7s 3c 4h 9c 2c 8d 8s Ac 7d 8c Kh 9s
Jh 6c 3h 5h
Cleared the deck.
Deck:
Press the enter key to exit.
Here's the program that produces that.
|
Do the Card and Hand classes stay the same?
|
# main
deck1 = Deck()
print "Created a new deck."
print "Deck:"
print deck1
deck1.populate()
print "\nPopulated the deck."
print "Deck:"
print deck1
deck1.shuffle()
print "\nShuffled the deck."
print "Deck:"
print deck1
my_hand = Hand()
your_hand = Hand()
hands = [my_hand, your_hand]
deck1.deal(hands, per_hand = 5)
print "\nDealt 5 cards to my hand and your hand."
print "My hand:"
print my_hand
print "Your hand:"
print your_hand
print "Deck:"
print deck1
deck1.clear()
print "\nCleared the deck."
print "Deck:", deck1
raw_input("\n\nPress the enter key to exit.")
Yes, but as you can see, there's a new class.
|
Deck . I like that.
|
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 rounds 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!"
Notice how a Deck is just a type of Hand .
|
That's what I was saying.
|
Everything else is the same.
|
Nice. What's the third version doing?
|
The out put is a bit cryptic.
|
Well, then let's look at the code.
|
Printing a Card object:
Ac
Printing an Unprintable_Card object:
Printing a Positionable_Card object:
Ah
Flipping the Positionable_Card object.
Printing the Positionable_Card object:
XX
Press the enter key to exit.
Good idea. Here's the main part of it.
|
Well, it looks like there's more to it than just that.
|
#main
card1 = Card("A", "c")
card2 = Unprintable_Card("A", "d")
card3 = Positionable_Card("A", "h")
print "Printing a Card object:"
print card1
print "\nPrinting an Unprintable_Card object:"
print card2
print "\nPrinting a Positionable_Card object:"
print card3
print "Flipping the Positionable_Card object."
card3.flip()
print "Printing the Positionable_Card object:"
print card3
raw_input("\n\nPress the enter key to exit.")
That's true.
|
I see new types of objects there.
|
Correct. Which one do you want to see first?
|
How do you define an Unprintable_Card ?
|
class Unprintable_Card(Card):
""" A Card that won't reveal its rank or suit when printed. """
def __str__(self):
return ""
We've already been through a lot of code so far.
|
So what, I want to know what a Positionable_Card looks like.
|
class Positionable_Card(Card):
""" A Card that can be face up or face down. """
def __init__(self, rank, suit, face_up = True):
super(Positionable_Card, self).__init__(rank, suit)
self.is_face_up = face_up
def __str__(self):
if self.is_face_up:
rep = super(Positionable_Card, self).__str__()
else:
rep = "XX"
return rep
def flip(self):
self.is_face_up = not self.is_face_up
It's a Card that can be flipped.
|
There you go!
|
So printing it and flipping it and printing it again shows.
|
The back of the card is marked "XX".
|
Very clear now.
|
We're almost there, step by step. What comes next?
|
Oh, man---I hope you realize this is just a brief description of things to come.
|
Yes, but let them come. So what's next in our step by step development?
|
Here's a simple game being played between some adults.
|
Oh. Who is Ben?
|
Welcome to the world's simplest game!
How many players? (2 - 5): 3
Player name: Rick
Player name: Larry
Player name: Reggie
Here are the game results:
Rick: 26
Larry: 71
Reggie: 53
Do you want to play again? (y/n): y
How many players? (2 - 5): 4
Player name: Ben
Player name: Reggie
Player name: Rick
Player name: Larry
Here are the game results:
Ben: 73
Reggie: 70
Rick: 19
Larry: 73
Do you want to play again? (y/n): n
Press the enter key to exit.
You don't know him. A friend of mine, he moved to Chicago this past summer.
|
I never met him. He looks intimidating.
|
He plays good defense.
|
Is he a lawyer?
|
More like an ambassador.
|
Good for him. Let's see the source code now.
|
import games, random
print "Welcome to the world's simplest game!\n"
again = None
while again != "n":
players = []
num = games.ask_number(question = "How many players? (2 - 5): ",
low = 2, high = 5)
for i in range(num):
name = raw_input("Player name: ")
score = random.randrange(100) + 1
player = games.Player(name, score)
players.append(player)
print "\nHere are the game results:"
for player in players:
print player
again = games.ask_yes_no("\nDo you want to play again? (y/n): ")
raw_input("\n\nPress the enter key to exit.")
It's imperative now that we present the games module.
|
I think so.
|
class Player(object):
""" A player for a game. """
def __init__(self, name, score = 0):
self.name = name
self.score = score
def __str__(self):
rep = self.name + ":\t" + str(self.score)
return rep
def ask_yes_no(question):
"""Ask a yes or no question."""
response = None
while response not in ("y", "n"):
response = raw_input(question).lower()
return response
def ask_number(question, low, high):
"""Ask for a number within a range."""
response = None
while response not in range(low, high):
response = int(raw_input(question))
return response
if __name__ == "__main__":
print "You ran this module directly (and did not 'import' it)."
raw_input("\n\nPress the enter key to exit.")
Ok. That would be enough for one lecture.
|
I think so too.
|
Then let's think of it all and resume it on Thursday.
|
Sounds like a great idea to me.
|
Updated by Adrian German for A202/A598