Homework 8
Due at 11:59:59 pm on Friday, 11/6/2020.
Instructions
Download hw08.zip. Inside the archive, you will find starter files for the questions in this homework, along with a copy of the OK autograder.
Submission: When you are done, submit with python3 ok --submit
. You may submit more than once before the deadline; only the final submission will be scored. Check that you have successfully submitted your code on okpy.org. See this article for more instructions on okpy and submitting assignments.
Readings: This homework relies on following references:
Inheritance
Question 1: Errors
It is often said that nothing in life is certain but death and taxes. For a programmer or data scientist, however, nothing is certain but encountering errors.
In Python, there are two primary types of errors, both of which you are likely familiar with: syntax errors and exceptions. Syntax errors occur when the proper structure of the language is not followed, while exceptions are errors that occur during the execution of a program. These include errors such as ZeroDivisionError, TypeError, NameError, and many more!
Under the hood, these errors are based in the concepts of object orientation, and all exceptions are class objects. If you're interested in more detailed explanations of the structure of exceptions as well as how to create your own, check out this article from the Python documentation! In the meantime, we'll implement our own version of an Error
class
Complete the Error
, SyntaxError
, and ZeroDivisionError
classes such that
they create the correct messages when called.
- The
SyntaxError
andZeroDivisionError
classes inherit from theError
class and add functionality that is unique to those particular errors. Their code is partially implemented for you. - The
add_code
method adds a new helpful message to your error, while thewrite
method should print the output that you see when an error is raised. - You can access the parent class methods using the super() function
class Error:
"""
>>> err1 = Error(12, "error.py")
>>> err1.write()
error.py:12
"""
def __init__(self, line, file):
"*** YOUR CODE HERE ***"
def format(self):
return self.file + ':' + str(self.line)
def write(self):
print(self.format())
class SyntaxError(Error):
"""
>>> err1 = SyntaxError(17, "lab10.py")
>>> err1.write()
lab10.py:17 SyntaxError : Invalid syntax
>>> err1.add_code(4, "EOL while scanning string literal")
>>> err2 = SyntaxError(18, "lab10.py", 4)
>>> err2.write()
lab10.py:18 SyntaxError : EOL while scanning string literal
"""
type = 'SyntaxError'
msgs = {0 : "Invalid syntax", 1: "Unmatched parentheses", 2: "Incorrect indentation", 3: "missing colon"}
def __init__(self, line, file, code=0):
"*** YOUR CODE HERE ***"
def format(self):
"*** YOUR CODE HERE ***"
def add_code(self, code, msg):
"*** YOUR CODE HERE ***"
class ZeroDivisionError(Error):
"""
>>> err1 = ZeroDivisionError(273, "lab10.py")
>>> err1.write()
lab10.py:273 ZeroDivisionError : division by zero
"""
type = 'ZeroDivisionError'
def __init__(self, line, file, message='division by zero'):
"*** YOUR CODE HERE ***"
def format(self):
end = self.type + ' : ' + self.message
"*** YOUR CODE HERE ***"
Use OK to test your code:
python3 ok -q Error
Use OK to test your code:
python3 ok -q SyntaxError
Use OK to test your code:
python3 ok -q ZeroDivisionError
Election
Let's implement a game called Election. In this game, two players compete to try and earn the most votes. Both players start with 0 votes and 100 popularity.
The two players alternate turns, and the first player starts. Each turn, the current player chooses an action. There are two types of actions:
- The player can debate, and either gain or lose 50 popularity. If the player
has popularity
p1
and the other player has popularityp2
, then the probability that the player gains 50 popularity ismax(0.1, p1 / (p1 + p2))
Note that themax
causes the probability to never be lower than 0.1. - The player can give a speech. If the player has popularity
p1
and the other player has popularityp2
, then the player gainsp1 // 10
votes and popularity and the other player losesp2 // 10
popularity.
The game ends when a player reaches 50 votes, or after a total of 10 turns have been played (each player has taken 5 turns). Whoever has more votes at the end of the game is the winner!
Question 2: Player
First, let's implement the Player
class. Fill in the debate
and speech
methods, that take in another Player
other
, and implement the correct
behavior as detailed above. Here are two additional things to keep in mind:
- In the
debate
method, you should call the providedrandom
function, which returns a random float between 0 and 1. The player should gain 50 popularity if the random number is smaller than the probability described above, and lose 50 popularity otherwise. - Neither players' votes or popularity should ever become negative. If this happens, set it equal to 0 instead.
### Phase 1: The Player Class
class Player:
"""
>>> reset_random()
>>> p1 = Player('Hill')
>>> p2 = Player('Don')
>>> p1.popularity
100
>>> p1.debate(p2) # random() should return 0.0
>>> p1.popularity
150
>>> p2.popularity
100
>>> p2.votes
0
>>> p2.speech(p1)
>>> p2.votes
10
>>> p2.popularity
110
>>> p1.popularity
135
>>> # Additional correctness tests
>>> p1.speech(p2)
>>> p1.votes
13
>>> p1.popularity
148
>>> p2.votes
10
>>> p2.popularity
99
>>> for _ in range(4): # 0.1, 0.2, 0.3, 0.4
... p1.debate(p2)
>>> p2.debate(p1)
>>> p2.popularity
49
>>> p2.debate(p1)
>>> p2.popularity
0
"""
def __init__(self, name):
self.name = name
self.votes = 0
self.popularity = 100
def debate(self, other):
"*** YOUR CODE HERE ***"
def speech(self, other):
"*** YOUR CODE HERE ***"
def choose(self, other):
return self.speech
Use OK to test your code:
python3 ok -q Player
Question 3: Game
Now, implement the Game
class. Fill in the play
method, which should
alternate between the two players, starting with p1
, and have each player take
one turn at a time. The choose
method in the Player
class returns the
method, either debate
or speech
, that should be called to perform the
action.
In addition, fill in the winner
property method, which should return the
player with more votes, or None
if the players are tied.
### Phase 2: The Game Class
class Game:
"""
>>> p1, p2 = Player('Hill'), Player('Don')
>>> g = Game(p1, p2)
>>> winner = g.play()
>>> p1 is winner
True
>>> # Additional correctness tests
>>> winner is g.winner
True
>>> g.turn
10
>>> p1.votes = p2.votes
>>> print(g.winner)
None
"""
def __init__(self, player1, player2):
self.p1 = player1
self.p2 = player2
self.turn = 0
def play(self):
while not self.game_over:
"*** YOUR CODE HERE ***"
return self.winner
@property
def game_over(self):
return max(self.p1.votes, self.p2.votes) >= 50 or self.turn >= 10
@property
def winner(self):
"*** YOUR CODE HERE ***"
Use OK to test your code:
python3 ok -q Game
Question 4: New Players
The choose
method in the Player
class is boring, because it always returns
the speech
method. Let's implement two new classes that inherit from Player
,
but have more interesting choose
methods.
Implement the choose
method in the AggressivePlayer
class, which returns the
debate
method if the player's popularity is less than or equal to other
's
popularity, and speech
otherwise. Also implement the choose
method in the
CautiousPlayer
class, which returns the debate
method if the player's
popularity is 0, and speech
otherwise.
### Phase 3: New Players
class AggressivePlayer(Player):
"""
>>> reset_random()
>>> p1, p2 = AggressivePlayer('Don'), Player('Hill')
>>> g = Game(p1, p2)
>>> winner = g.play()
>>> p1 is winner
True
>>> # Additional correctness tests
>>> p1.popularity = p2.popularity
>>> p1.choose(p2) == p1.debate
True
>>> p1.popularity += 1
>>> p1.choose(p2) == p1.debate
False
>>> p2.choose(p1) == p2.speech
True
"""
def choose(self, other):
"*** YOUR CODE HERE ***"
class CautiousPlayer(Player):
"""
>>> reset_random()
>>> p1, p2 = CautiousPlayer('Hill'), AggressivePlayer('Don')
>>> p1.popularity = 0
>>> p1.choose(p2) == p1.debate
True
>>> p1.popularity = 1
>>> p1.choose(p2) == p1.debate
False
>>> # Additional correctness tests
>>> p2.choose(p1) == p2.speech
True
"""
def choose(self, other):
"*** YOUR CODE HERE ***"
Use OK to test your code:
python3 ok -q AggressivePlayer
python3 ok -q CautiousPlayer