Problem overview
A game shows each player two numbers: the total of all their turns and their best single turn. The class ScoreKeeper keeps both up to date. add(points) records one turn, total() and best() report the two numbers, and reset() starts over. Before any add, both numbers are 0.
In the first example, the turns 10, 25 and 5 give a total of 40 and a best of 25. In the third example the keeper adds 30 and 50, reports 80 and 50, resets, reports 0 and 0, then adds 20 and reports a best of 20 and a total of 20. The 50 from before the reset no longer counts.
Approach: update two numbers as the points arrive
You could store every turn in a list and compute the sum and the maximum when asked. That works, but each call would look through all the old turns. A simpler idea is to keep only two numbers on the object and update them in add. Then total() and best() just report a stored value, and all four methods run in O(1) time.
The best starts at 0. Every score is at least 0, so 0 is a safe starting value: the first add can only match or beat it.
Why the attributes are not called total and best
The attributes are named total_points and best_points, not total and best. In Python an attribute set on the object hides a method of the same name. If the constructor did self.total = 0, then keeper.total() would try to call the number 0 and fail with 'int' object is not callable.
Step by step
- Set
self.total_pointsto 0 in__init__: a new keeper has seen no points. - Set
self.best_pointsto 0: the highest single add so far. - In
add, add the points to the total with+=. The total grows with every turn. - Update the best only when the new score is higher. This is the same idea as finding the largest value in a list one value at a time. With
>, an equal score leaves the best unchanged, which is the same number anyway. totalreturnsself.total_points.bestreturnsself.best_points.resetsets both attributes back to 0, the values a new keeper starts with.
The solution in Python
class ScoreKeeper:
def __init__(self):
self.total_points = 0
self.best_points = 0
def add(self, points: int) -> None:
self.total_points += points
if points > self.best_points:
self.best_points = points
def total(self) -> int:
return self.total_points
def best(self) -> int:
return self.best_points
def reset(self) -> None:
self.total_points = 0
self.best_points = 0
Using it:
k = ScoreKeeper()
print(k.total(), k.best())
for p in (30, 50):
k.add(p)
print(k.total(), k.best())
k.reset()
print(k.total(), k.best())
k.add(20)
print(k.best(), k.total())
0 0
80 50
0 0
20 20
Edge cases and common mistakes
- Asking before any add. Both methods return 0, because the constructor set both attributes to 0.
- An add of 0. The total stays the same and, since 0 is not greater than the best, so does the best.
- Forgetting to reset the best. If
resetonly clears the total, the old best of 50 survives andbest()returns 50 afteradd(20)instead of 20. - Naming an attribute like a method. It breaks the method, as shown above.
- Using
=instead of+=inadd. The total would then hold only the last turn.
