**Short Assignment 6: Counters and Timers** This assignment will give you a little practice in defining and working with classes. It uses no graphics. # A `Counter` class Your first task is to define a class `Counter` for a counter that counts down. When it gets down to 0 and counts down again, it wraps back to a limit, minus 1. For example, if the limit is 60, the counter's value is 0, and it counts down, its next value is 59. The `Counter` class must support the following methods: - `__init__(self, limit, initial = 0, min_digits = 1)` is automatically called by the `Counter` constructor (but you knew that, right?). The parameters are the counter's limit (60 in the example above), an initial value, and a minimum number of digits (explained below). If the initial value supplied as a parameter is between 0 and limit–1, then that's the counter's initial value. Otherwise, an error message is printed and the counter's initial value is limit–1. - `get_value(self)` just returns the counter's value, as an int. - `__str__(self)` returns the counter's value, as a string. If the counter's value would print with fewer than the `min_digits` parameter supplied to the `__init__` method, then the string contains `min_digits` characters, padded on the left with zeros. For example, if `min_digits` is 4 and the counter's value is 15, then the `__str__` method should return the string `0015`. - `tick(self)` decrements the counter's value, but if the value would go negative, then it becomes limit–1. For example, if the counter's limit is 12 and its current value is 0, then after calling `tick`, the counter's value should be 11. This method also returns to its caller a boolean value indicating whether it wrapped around, i.e., did the value wrap from 0 back to limit–1. `True` means that it wrapped, `False` means that it did not. Define instance variables as appropriate, though I'd be surprised if you needed anything other than three. To pad with zeros in the `__str__` method, you can determine the length of the counter's value as a string (by calling the `str` function on the counter's value), and then concatenate as many zeros as necessary (the difference between `min_digits` and the length of this string). There are a couple of ways to create a string of a given number of `0`s; let's say we want to create a string of *n* `0`s. The first way is to use a loop. Start with an empty string, and repeatedly concatenate a `0` *n* times. The second way uses another cool feature of Python. If you want a list or string that is just a value repeated a number of times, you can use the `*` operator. For example, `'0' * 4` gives the string `0000`, because it says to repeat the string `0` four times. If the repeat count is zero or negative, you get an empty string (which is just what you want here). This operator works for lists, too, so that `[0] * 4` gives the list `[0, 0, 0, 0]`, though you won't need to work with lists for this assignment. # A `Counter` driver A **driver** is a piece of code that exists solely to exercise some other piece of code. A driver is typically not interesting in and of itself. Write a driver for the `Counter` class. It should create two or more `Counter` objects and demonstrate that all the methods in the `Counter` class work properly by printing to the console. # A `Timer` class Your next task is to define a class `Timer` for a 24-hour hours:minutes:seconds timer that counts down. It must have instance variables that reference `Counter` objects for the hours, minutes, and seconds. Your `Timer` class must support the following methods: - `__init__(self, hours, minutes, seconds)` creates a timer whose initial values for its hours, minutes, and seconds are given by the parameters. This method must create three `Counter` objects and save the references given back by the `Counter` constructors in instance variables for the `Timer` object. If any of the values given for hours, minutes, or seconds is out of range, then the initial value used should be the value that the corresponding `Counter` object would use. - `__str__(self)` returns a string giving the timer's current time, in the format hh:mm:ss. That is, two digits for the hours, followed by a colon, followed by two digits for the minutes, followed by a colon, followed by two digits for the seconds. For example, if the timer currently reads 1 hour, 7 minutes, and 52 seconds, `__str__` should return the string `01:07:52`. - `tick(self)` ticks down the timer by one second. For example, if a timer reads `01:07:52`, then after calling `tick`, it should read `01:07:51`. If a timer reads `01:07:00`, then after calling `tick`, it should read `01:06:59`. If a timer reads `01:00:00`, then after calling `tick`, it should read `00:59:59`. Finally, if a timer reads `00:00:00`, then after calling `tick`, it should read `23:59:59`. - `is_zero(self)` returns a boolean that is `True` if the timer currently reads `00:00:00`. These methods should call the appropriate methods on the appropriate `Counter` objects. ## Important note about the `tick` method of the `Timer` class Remember that the `tick` method of `Counter` not only ticks the `Counter` down, but it also returns a boolean telling you whether the `Counter` wrapped. If you call the `tick` method of `Counter` on the seconds counter, and this call returns `True`, then you need to call the `tick` method on the minutes counter. And if you call the `tick` method of `Counter` on the minutes counter, and this call returns `True`, then you need to call the `tick` method on the hours counter. What about if you call the `tick` method of `Counter` on the hours counter, and this call returns `True`? It doesn't matter; you don't have to do anything when the hours counter wraps. Because each call of the `tick` method of `Counter` does two things—decrements the `Counter`'s value *and* returns a boolean telling you whether the `Counter` wrapped—you need *exactly one* call of `tick` for each of the three `Counter` objects each time you tick the `Timer`. I was able to write the body of the `tick` method of `Timer` in three lines, each containing a call of `tick` on a `Counter` (and possibly some other actions as well). # A `Timer` driver Finally, write a driver for the `Timer` class. It should start a timer at 01:30:00 and count it down to 00:00:00, printing to the console all the times between and including 01:30:00 and 00:00:00. # What to turn in Turn in `counter.py`, containing your `Counter` class; `timer.py`, containing your `Timer` class; your driver for `Counter`; and your driver for `Timer`. Also turn in text files containing the console output from your `Counter` driver and `Timer` driver. # Honor Code The consequences of violating the Honor Code can be severe. Please always keep in mind the word and spirit of the Honor Code.