This means:
- A lottery ticket needs contents and a place to hold the prize
- A lottery ticket starts off empty or "not revealed"
- When a lottery ticket is revealed, the contents are set on the ticket
- Having the contents, one can then determine the prize.
So with that in mind, I create my lottery ticket superclass:
class LotteryTicket():
""" A class to represent a lottery ticket """
def __init__(self):
""" (LotteryTicket) -> NoneType
Initialize a lottery ticket to contain contents and prize
>>> lot = LotteryTicket()
>>> isinstance(lot, LotteryTicket)
True
>>> lot.contents
''
>>> lot.prize
0
"""
self.contents = ''
self.prize = 0
Here, it is noted that contents and prize are set right from the get-go, though since we are not sure what the contents will be, we leave it as a blank string. Prize is also set to 0 since we are not aware of what it will be based on the contents.
def __str__(self):
""" (LotteryTicket) -> str
Return a string representation of LotteryTicket
>>> lot = LotteryTicket()
>>> print(lot)
LotteryTicket
Contents:
Prize: 0
"""
return "LotteryTicket\nContents: {0}\nPrize: {1}".format(self.contents, self.prize)
I've defined the __str__ method more to give an idea of what a generic ticket should look like.
def __repr__(self):
""" (LotteryTicket) -> str
Return a shell representation of LotteryTicket
"""
raise NotImplementedError('Subclass needed')
def __eq__(self, other):
""" (LotteryTicket, LotteryTicket) -> bool
Return whether two LotteryTickets are the same
"""
raise NotImplementedError('Subclass needed')
def set_ticket(self):
""" (LotteryTicket) -> NoneType
Set the contents of LotteryTicket
"""
raise NotImplementedError('Subclass needed')
def get_prize(self):
""" (LotteryTicket) -> NoneType
Obtain prize based on the contents of LotteryTicket
"""
raise NotImplementedError('Subclass needed')
All of these methods have not been implemented but they are crucial to the skeleton of a lottery ticket so they've been included for the sake of reference and convenience.
Now that I have a basic representation of a generic lottery ticket dubbed LotteryTicket, I can now start to think of making more specific LotteryTickets. There are many ways I can go about making more specific lottery tickets and I have even managed to come up with a few. But for the sake of convenience, I'm going to pool all of these ideas into one LotteryTicket subclass I'll call MegaWinningsTicket.
from LotteryTicket import LotteryTicket
import random
class MegaWinningsTicket(LotteryTicket):
""" A lottery ticket with Mega Winnings!
Price of MegaWinningsTicket determine which game you play!
5 - Pick 2
Try to get the winning two numbers!
10 - Pick 3
Try to get the winning three numbers!
15 - Crossword
Try to get the necessary letters to complete the crossword!
20 - Golden Diamond
Try to get the elusive Golden Diamond!
"""
def __init__(self, price):
""" (MegaWinningsTicket) -> NoneType
Precondition: price must be in [5, 10, 15, 20]
Initialize a MegaWinnings lottery ticket to contain contents and prize
and to hold the value of price
>>> mega = MegaWinningsTicket(5)
>>> isinstance(mega, MegaWinningsTicket)
True
>>> mega.contents
''
>>> mega.prize
0
>>> mega.price
5
"""
LotteryTicket.__init__(self)
self.price = price
Notice that now there is an added parameter in MegaWinningsTicket dubbed price. While it may not be advisable to name this parameter price since it is so close to prize, this little bit allows me to extend the __init__ method to suit this subclass. Specifically, the price allows you to play a certain game that the MegaWinningsTicket can support. Let's go through each of them in order.
- Pick 2
Price: 5
Contents: 2 numbers
Objective: Obtain two numbers that are exactly the same to the results
Prize: 2500 for both numbers
Odds of winning the big prize: 1 in 100 - Pick 3
Price: 10
Contents: 3 numbers
Objective: Obtain three numbers that are exactly the same to the results
Prize: 10000 for complete match (big prize), 5000 for two numbers that match
Odds of winning the big prize: 1 in 1000 - Crossword
Price: 15
Contents: 10 letters
Objective: To get all the words in this crosswordrc a brb e l la ar be r rPrize: 50000 for all words removed (big prize), 100 for 5 words removed, 50 for 4 words removed, 25 for 3 words removed, 10 for 2 words removed, 2 for one removed
Odds of winnings the big prize: 1 in 25294 (5311735 possible combinations / 210 that contain the letters c, a, b, r, e, l) - Golden Diamond
Price: 20
Contents: One of the following objects: Golden Diamond, Diamond, Gold, Zirconium, Pyrite, Gold-Painted Rock, Shiny Rock
Objective: Obtain a golden diamond
Prize: 10000000 for a golden diamond (big prize), 500000 for a diamond, 100000 for gold, 1000 for zirconium, 20 for pyrite
Odds of winning the big prize: 1 in 111111 (1 Golden Diamond + 10 Diamonds + 100 Gold + 1000 Zirconium + 10000 Pyrite + 50000 Gold-Painted Rocks + 50000 Shiny Rocks)
Each of these will be shown in more detail but one can see that there will have to be an implementation of __eq__ that is specific to this function
def __str__(self):
""" (MegaWinningsTicket) -> str
Return a string representation of MegaWinningsTicket
>>> mega = MegaWinningsTicket(5)
>>> print(mega)
MegaWinningsTicket
Contents:
Prize: 0
Price: 5
"""
return "MegaWinningsTicket\nContents: {0}\nPrize: {1}\nPrice: {2}".format(self.contents, self.prize, self.price)
For the __str__ method, I overwrote the method so that it would also include the price in the string.
def __repr__(self):
""" (MegaWinningsTicket) -> str
Return a shell representation of MegaWinningsTicket
>>> mega = MegaWinningsTicket(5)
>>> mega
MegaWinningsTicket(, 0, 5)
"""
return "MegaWinningsTicket({0}, {1}, {2})".format(self.contents, self.prize, self.price)
I have implemented the __repr__ method more as practice in doing so since there is no purpose it serves in the code.
def __eq__(self, other):
""" (MegaWinningsTicket, MegaWinningsTicket) -> bool
Return whether two MegaWinningsTickets are the same
>>> mega = MegaWinningsTicket(5)
>>> winnings = MegaWinningsTicket(5)
>>> ticket = MegaWinningsTicket(10)
>>> mega == winnings
True
>>> mega == ticket
False
>>> mega.contents = '3'
>>> mega == winnings
False
>>> winnings.contents = '3'
>>> winnings.prize = 20
>>> mega == winnings
True
"""
return self.contents == other.contents and self.price == other.price
Here, I've made it clear that contents have to be the same as well as price (which isn't necessary in the following code, but asserts that the tickets belong to the same game). This brings up a question as to why I didn't have this implemented in the LotteryTicket superclass:
return self.contents == other.contents
Well that is because the way that certain tickets and games conduct themselves may rely on checking for equality in different ways, which may be a matter of parameters that the LotteryTicket subclass contains or what is being checked in contents.
def set_ticket(self):
""" (MegaWinningsTicket) -> NoneType
Set the contents of MegaWinningsTicket based on price
"""
number_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
if self.price == 5: # Pick 2
self.contents = [random.choice(number_list), random.choice(number_list)]
if self.price == 10: # Pick 3
self.contents = [random.choice(number_list), random.choice(number_list), random.choice(number_list)]
if self.price == 15: # Crossword
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
words = ['cab', 'bell', 'rare', 'err', 'bare', 'lab']
print('The words are: {}'.format(words))
letters = []
for i in range(10):
letter = random.choice(alphabet)
letters.append(letter)
alphabet.remove(letter)
print('Your letters are: {}'.format(letters))
new_words = []
for word in words:
for letter in letters:
word = word.replace(letter, '')
new_words.append(word)
print(new_words)
self.contents = new_words.count('')
if self.price == 20: #Golden Diamond
gem_list = ['Golden Diamond'] + ['Diamond'] * 10 + ['Gold'] * 100 + ['Zirconium'] * 1000 + ['Pyrite'] * 10000 + ['Gold-Painted Rock'] * 50000 + ['Shiny Rock'] * 50000
self.contents = random.choice(gem_list)
Now this is where we get to the implementation of an important part of the LotteryTicket subclass, that being the set_ticket method which "reveals" the ticket by setting the contents depending on the price. For Pick 2, a list of two numbers is set. For Pick 3, a list of three numbers is set. For Crossword, the amount of words removed is set. And lastly, for Golden Diamond, a string is set. Having contents set will then help us to figure out how to get the prize.
def get_prize(self):
""" (MegaWinningsTicket) -> NoneType
Precondition: self.price == 15 or self.price == 20 and MegaWinningsTicket
has been given contents by the set_ticket method
Obtain prize based on the contents of MegaWinningsTicket
"""
if self.price == 15:
possible_winnings = [0, 2, 10, 25, 50, 100, 50000]
self.prize = possible_winnings[self.contents]
if self.price == 20:
possible_winnings = {'Gold Diamond': 10000000, 'Diamond': 500000, 'Gold': 100000, 'Zirconium': 1000, 'Pyrite': 20, 'Gold-Painted Rock': 0, 'Shiny Rock': 0}
self.prize = possible_winnings[self.contents]
Notice how in the docstring, it specifically indicates that set_ticket must have been run on the MegaWinningsTicket for get_prize to properly work. For Crossword, the contents is used as an index on the list possible_winnings. For example, if 2 words were removed, 2 is the contents. And if 2 is the contents, the prize is possible_winnings[2] which equals 10, thus getting the prize and setting it. Golden Diamond works similarly with the contents serving as a key in the dict possible_winnings. These approaches do not rely on another ticket as the results, so therefore Pick 2 and Pick 3 need their own method
def pick_prize(self, results):
""" (MegaWinningsTicket, MegaWinningsTicket) -> NoneType
Precondition: self.price == 5 or self.price == 10 and both MegaWinningsTickets
has been given contents by the set_ticket method
Obtain prize based on how well the ticket compares with results
>>> mega = MegaWinningsTicket(5)
>>> results = MegaWinningsTicket(5)
>>> mega.contents = [4, 2]
>>> results.contents = [4, 2]
>>> mega.pick_prize(results)
>>> mega.prize
2500
>>> mega.contents = [0, 2]
>>> mega.pick_prize(results)
>>> mega.prize
0
>>> mega = MegaWinningsTicket(10)
>>> results = MegaWinningsTicket(10)
>>> mega.contents = [1, 2, 3]
>>> results.contents = [1, 2, 3]
>>> mega.pick_prize(results)
>>> mega.prize
10000
>>> mega.contents = [1, 2, 4]
>>> mega.pick_prize(results)
>>> mega.prize
5000
>>> mega.contents = [1, 5, 4]
>>> mega.pick_prize(results)
>>> mega.prize
0
"""
if self.price == 5:
if self == results:
self.prize = 2500
else:
self.prize = 0
if self.price == 10:
numbers = list(results.contents)
for num in self.contents:
if num in numbers:
numbers.remove(num)
if len(numbers) == 0:
self.prize = 10000
if len(numbers) == 1:
self.prize = 5000
if len(numbers) >= 2:
self.prize = 0
Pick 2 was easy to implement since it just relies on using what we wrote on with __eq__. Pick 3 needed to check if at the very least 2 numbers were the same, so we couldn't have simply used the __eq__ approach. Though we could have if the conditions were equally as harsh.
def is_mega_winner(self):
""" (MegaWinningsTicket) -> str
Return a string indicating if MegaWinningsTicket is a winner
>>> mega = MegaWinningsTicket(5)
>>> mega.is_mega_winner()
'Sorry, try again'
>>> mega.prize = 1000
>>> mega.is_mega_winner()
'Sorry, try again'
>>> mega.prize = 2500
>>> mega.is_mega_winner()
'MEGA WINNER!'
>>> mega = MegaWinningsTicket(10)
>>> mega.is_mega_winner()
'Sorry, try again'
>>> mega.prize = 10000
>>> mega.is_mega_winner()
'MEGA WINNER!'
>>> mega = MegaWinningsTicket(15)
>>> mega.is_mega_winner()
'Sorry, try again'
>>> mega.prize = 50000
>>> mega.is_mega_winner()
'MEGA WINNER!'
>>> mega = MegaWinningsTicket(20)
>>> mega.is_mega_winner()
'Sorry, try again'
>>> mega.prize = 100000
>>> mega.is_mega_winner()
'MEGA WINNER!'
>>> mega.prize = 10000000
>>> mega.is_mega_winner()
'ULTIMATE MEGA WINNER! CONGRATULATIONS'
"""
if self.prize == 10000000:
return 'ULTIMATE MEGA WINNER! CONGRATULATIONS'
elif 10000000 > self.prize >= 2500:
return 'MEGA WINNER!'
else:
return 'Sorry, try again'
Last and not least is a simple method that tells us whether or not the prize we have is worth of a mega win. A mega win is dictated by if we get a prize who is larger than 2500. Though it we were to get the Golden Diamond in Golden Diamond, we would receive a more congratulatory string.
There's so many other subclasses of LotteryTicket we can create, each having their own implementations of the methods necessary for the class, some which may overwrite the code, others which may extend it. Though one thing is for sure...trying to win these games might not be as simple.
No comments:
Post a Comment