Python 112 - Thumper
I think we should break out the thumper logic into a Flyer and simplify Fleets accordingly.
Fleets makes the increasingly scary “thumper” sound with this code:
class Fleets:
def __init__(self, asteroids=(), missiles=(), saucers=(), saucer_missiles=(), ships=()):
...
self.thumper = Thumper(self.beat1, self.beat2)
def tick(self, delta_time):
if self._asteroids and self._ships:
self.thumper.tick(delta_time)
else:
self.thumper.reset()
self.flyers.tick(delta_time, self)
@staticmethod
def beat1():
player.play("beat1")
@staticmethod
def beat2():
player.play("beat2")
The Thumper object is just this:
class Thumper:
def __init__(self, b1, b2):
self._long_interval = 30/60
self._short_interval = 8/60
self._decrement_interval = 127/60
self.b1 = b1
self.b2 = b2
self.reset()
# noinspection PyAttributeOutsideInit
def reset(self):
self._interval = self._long_interval
self._decrement_time = 0
self._execute_time = 0
# noinspection PyAttributeOutsideInit
def tick(self, delta_time):
self._execute_time += delta_time
if self._execute_time >= self._interval:
self._execute_time = 0
self.b1()
self.b1, self.b2 = self.b2, self.b1
self._decrement_time += delta_time
if self._decrement_time >= self._decrement_interval:
self._decrement_time = 0
self._interval = self._interval - 1/60
if self._interval < 8/60:
self._interval = 8/60
What the thing does, and I don’t like the names much here, is that hmm it needs to use that _short_interval
member, not the literal 8/60
. As I was saying …
- The value
_interval
starts at_long_interval
and over time gets decremented down to_short_interval
; - Every
_interval
seconds, it plays one of the beats and swaps them so that next time it will play the other. Bump BUMP Bump BUMP. - Every
_decrement_interval
seconds, it shortens the_interval
by a sixtieth of a second, making the beats come closer and closer together.
Let’s rename the variables and use them where they need it.
OK, I went wild with names and extracts. Here it is now:
class Thumper:
def __init__(self, b1, b2):
self._longest_time_between_beats = 30 / 60
self._shortest_time_between_beats = 8 / 60
self._delay_before_shortening_beat_time = 127 / 60
self._amount_to_shorten_beat_time = 1 / 60
self._time_between_beats = 0
self._time_since_last_beat = 0
self._time_since_last_decrement = 0
self.b1 = b1
self.b2 = b2
self.reset()
def reset(self):
self._time_between_beats = self._longest_time_between_beats
self._time_since_last_decrement = 0
self._time_since_last_beat = 0
def tick(self, delta_time):
if self.it_is_time_to_beat(delta_time):
self.play_and_reset_beat()
if self.it_is_time_to_speed_up_beats(delta_time):
self.speed_up_beats()
def it_is_time_to_beat(self, delta_time):
self._time_since_last_beat += delta_time
return self._time_since_last_beat >= self._time_between_beats
def play_and_reset_beat(self):
self._time_since_last_beat = 0
self.b1()
self.b1, self.b2 = self.b2, self.b1
def it_is_time_to_speed_up_beats(self, delta_time):
self._time_since_last_decrement += delta_time
return self._time_since_last_decrement >= self._delay_before_shortening_beat_time
def speed_up_beats(self):
self._time_since_last_decrement = 0
self._time_between_beats =
self._time_between_beats - self._amount_to_shorten_beat_time
if self._time_between_beats < self._shortest_time_between_beats:
self._time_between_beats = self._shortest_time_between_beats
Let’s simplify that last bit.
def speed_up_beats(self):
self._time_since_last_decrement = 0
self._time_between_beats = max(
self._time_between_beats - self._amount_to_shorten_beat_time,
self._shortest_time_between_beats)
OK, that’s about the best I can do. Commit: refactor Thumper to death.
Now let’s move the beats inside.
class Thumper:
def __init__(self, b1=None, b2=None):
self._longest_time_between_beats = 30 / 60
self._shortest_time_between_beats = 8 / 60
self._delay_before_shortening_beat_time = 127 / 60
self._amount_to_shorten_beat_time = 1 / 60
self._time_between_beats = 0
self._time_since_last_beat = 0
self._time_since_last_decrement = 0
self.b1 = b1 if b1 else self.beat1
self.b2 = b2 if b2 else self.beat2
self.reset()
@staticmethod
def beat1():
player.play("beat1")
@staticmethod
def beat2():
player.play("beat2")
That works just fine. Commit: Thumper defaults to play beat1 and beat2.
I think I’ll stop here, as the day has become random.
Summary
We’ve taken a few steps toward making Thumper more clear and more self-contained, while keeping its tests running. We can turn it into a Flyer real soon now.