Let’s take the next steps to, um taking the next steps.

So, right. Our mission is to extend our nascent Space Invaders game so that the invaders can move back and forth and downward. When we’re done, we are supposed to move just one invader on each call to `update`, so that they sort of ripple across and down the screen. But we’ll begin by moving them all at once.

The “mix” will consist of our InvaderFleet, which contains 55 invaders to start with, and two Bumpers, one on each side. Let’s start with that setup. In `coin`:

``````coin.py
fleets.clear()
fleets.append(Bumper(16))
fleets.append(Bumper(u.SCREEN_SIZE - 16))
``````

A quick check just to see what explodes. Nothing does, so that’s good. Let’s see if I can devise a test for `update`.

The call to `update` comes every 60th of a second, and it’s passed `delta_time` and the current `fleets` instance. Our screen is 1024 wide and our “invaders” are currently spaced 64 pixels apart. In the original game, they are 16 pixels wide, abutting, and that’s on an approximately 256-bit pixel screen, so I think that if 64 is 4 times 16, that spacing is about right. So, let’s see, we’ll have 320 pixels of motion all the way to the edge, minus the 32 I’m allowing right now, so about 288 pixels to move one side to the other.

The videos of the game seem to take about 10 seconds side to side, so let’s say that’s 20 pixels per second. (We’ll have to set all these values into our universal constants in due time, of course.)

Let’s do a simple test just to get the ball rolling:

``````    def test_fleet_motion(self):
assert fleet.step == Vector2(30, 0)
fleet.at_edge()
fleet.end_interactions(None)
assert fleet.step == (Vector2(-30, 0))
``````

So a member, `step`, that’s either (30, 0) or (-30, 0), subject to reversal.

``````class InvaderFleet(Flyer):
def __init__(self):
self.origin = Vector2(u.SCREEN_SIZE / 2 - 5*64, 512)
self.step = Vector2(30, 0)
self.reverse = False
self.update(0, None)

def end_interactions(self, fleets):
if self.reverse:
self.reverse = False
self.step = -self.step
``````

Test is green. Commit: InvaderFleet has step and reverses it after `at_edge` is called.

Now test that we move an invader on `update`. I’ll just test the starting one so that when we do the ripple, this test will continue to work.

``````    def test_fleet_motion(self):
assert fleet.step == Vector2(30, 0)
fleet.update(1.0, None)
assert new_pos - pos == fleet.step
fleet.at_edge()
fleet.end_interactions(None)
assert fleet.step == (Vector2(-30, 0))
``````

That’s failing, first because the invader doesn’t know “position”.

``````    @property
def position(self):
return Vector2(self.rect.center)
``````

Still failing, I hope for a zero vector from the subtract.

``````Expected :<Vector2(30, 0)>
Actual   :<Vector2(0, 0)>
``````

Perfect! Now fix update to move the invaders:

``````    def update(self, delta_time, _fleets):
self.origin += self.step*delta_time
``````

I am not loving the fact that I set `delta_time` to 1.0, since I could have forgotten to multiply it in. Even worse, when I remove the multiply, the test fails. That rather surprises me.

Ah, it’s a different test. The error is:

``````    def test_fleet_origin(self):
>       assert fleet.origin == Vector2(u.SCREEN_SIZE / 2 - 5*64, 512)
E       assert <Vector2(222, 512)> == <Vector2(192, 512)>
E         Full diff:
E         - <Vector2(192, 512)>
E         ?          ^^
E         + <Vector2(222, 512)>
E         ?          ^^
``````

Why would `delta_time` affect that? Ah! We use update with a `delta_time` of zero to initialize our invaders positions. Without the multiply, we use `30`.

``````class InvaderFleet(Flyer):
def __init__(self):
self.origin = Vector2(u.SCREEN_SIZE / 2 - 5*64, 512)
self.step = Vector2(30, 0)
self.reverse = False
self.update(0, None)
``````

So the `delta_time` is necessary and tested.

``````    def update(self, delta_time, _fleets):
self.origin += self.step*delta_time
``````

Super. I think the invaders will move on the screen now. They do, but they move right off.

Ah, we aren’t handling the `interact_with_bumper` message in InvaderFleet. OK. I do that:

``````    def interact_with_bumper(self, bumper, _fleets):
``````

But Bumper doesn’t implement `interact_with`! My ignored tests might have told me that.

``````class Bumper(Flyer):
def __init__(self, x):
self.rect = Rect(x, 0, x+1, u.SCREEN_SIZE)

pass

def interact_with(self, other, fleets):
other.interact_with_bumper(self, fleets)
``````

That ought to do it. No … we need interact_with_bumper … there are two bumpers.

``````class Bumper(Flyer):
def __init__(self, x):
self.rect = Rect(x, 0, x+1, u.SCREEN_SIZE)

pass

def interact_with_bumper(self, bumper, fleets):
pass

def interact_with(self, other, fleets):
other.interact_with_bumper(self, fleets)
``````

And we are green. And we have this:

Let’s commit: Invaders move back and forth, as a unit, bouncing off bumpers. And let’s sum up.

## Summary

This is good progress. I’m sure the team will enjoy seeing the invader substitutes moving back and forth. What’s neat about it is that Bruce’s idea worked perfectly, as one would expect both because his ideas are good and because the decentralized design is just so nice for things like that.

We need to get rid of a lot of magic numbers, but I like that in the tests and even in the code, we kind of show our work in coming up with values.

I think our current Invader rectangle isn’t good, but we’ll deal with that as soon as we plug in the bitmaps. We may find it desirable to move the bumpers when we put in the real pictures, since they’ll collide with the blank space at the sides of the invaders: they have two blank columns on each side of their 16x8 basic layout. Easily tuned, I’m sure.

I think we’d be wise to create separate hierarchies for each game (Asteroids and Invaders, but who knows what we might do next), so that we can take advantage of the class methods in the upper interface, and so that we can fix up those two ignored tests that are checking for methods that our objects don’t really need. I think just making two subclasses of Flyer, AsteroidsFlyer and InvadersFlyer, with suitable abstract class methods, will do the job for the compiler, but the special inspection code in the tests probably needs reworking. We’ll see.

Definitely should be done, and probably rather tedious. I hope I have the gumption to do it.

Overall, it seems to be going well. Next time, maybe some refactoring, or maybe the ripple effect.

See you then!