Python Asteroids+Invaders on GitHub

Sometimes I get ideas that are really tasty but impractical. Let’s try something practical.

I’ve been talking about raycasting and a smart Driver, and using events to tell me what’s over the Driver/Player, and even about learning processes and such. Could be great fun, and in terms of learning interesting things, or just doing interesting things, they do have value. But really, can’t we do something simpler?

This morning I have in mind fixing up Driver so that it just runs its Player image over to a blank space between shields and fires at what’s above for a while. Probably forever, at least until we add some more code.

In the complicated side of my mind, I’m envisioning a list of moves to make, things to do, but that’s as far as I’ve pushed even that simple idea. For now, just that one behavior.

So the question is, where are the open and closed spaces under the shields? We could find that with raycasting or the like, or we could just observe this code:

coin.py
def invaders(fleets):
    fleets.clear()
    left_bumper = u.BUMPER_LEFT
    fleets.append(Bumper(left_bumper, -1))
    fleets.append(Bumper(u.BUMPER_RIGHT, +1))
    fleets.append(TopBumper())
    fleets.append(InvaderFleet())
    fleets.append(PlayerMaker())
    fleets.append(ShotController())
    fleets.append(InvaderScoreKeeper())
    fleets.append(RoadFurniture.bottom_line())
    fleets.append(TimeCapsule(10, InvadersSaucerMaker()))
    for i in range(3):
        fleets.append(ReservePlayer(i))
    half_width = 88 / 2
    spacing = 198
    step = 180
    for i in range(4):
        place = Vector2(half_width + spacing + i * step, u.SHIELD_Y)
        fleets.append(RoadFurniture.shield(place))

That last bit is deciding where to put the shields. Can’t we just print out some useful information while we do that?

    for i in range(4):
        place = Vector2(half_width + spacing + i * step, u.SHIELD_Y)
        fleets.append(shield := RoadFurniture.shield(place))
        rect = shield.rect
        print(i, rect.left, rect.right)
0 198 286
1 378 466
2 558 646
3 738 826

There we are. Roll the change back. I think I’ll just paste that info into Driver for safe-keeping.

class Driver(Spritely, InvadersFlyer):
    shield_locations = ((198, 286), (378, 466), (558, 646), (738, 826))

Now I’m going to hammer on Driver a bit. Recall that I’ve put one into the invaders attract mode coin.

I bash it like this:

    def update(self, delta_time, fleets):
        if self.rect.x < (378+286) / 2:
            centerx = self._sprite.centerx + self.step
            self.position = Vector2(centerx, self.position.y)

That works almost as intended. When the game over screen comes up, the Driver moves over. Unfortunately, it goes where I told it, not where I wanted it.

player too close to second shield

After far too much confusion, and a very long delay, I have this:

    def update(self, delta_time, fleets):
        if self._sprite.centerx < (378+286) / 2:
            centerx = self._sprite.centerx + self.step
            self.position = Vector2(centerx, self.position.y)
        self.count = (self.count + 1) % 60
        if not self.count:
            fleets.append(PlayerShot(self._sprite.center))

I was using the rectangle’s x coordinate, which is lower left, not center x. Once I finally saw that, and after pasting in a bit of firing, we get the driver moving between the first two shields and firing repeatedly.

As intended. At last.

I’d like a break to make my morning iced chai, so let’s sum up for now.

Commit: driver moves and shoots. Unlike panda, who eats shoots and leaves.1

Summary

I think my plan to do an ad hoc simple bit of driver code was a good one. If we eliminate the time I wasted using x instead of centerx, it went quite quickly and exactly as planned.

I can imagine just providing a simple list of commands for the Driver to interpret, move here, fire missile, move there, and that’s about it. We could even have some randomness in there pretty easily. No AI, but credible play. Then we could start making it smarter.

So the larger lesson here is one that I need to learn almost every day: “No, simpler than that”. It’s OK to have grand desires and expectations. I still want to see how smart we can make the attract mode driver. But come on, Ron, start with something simple! No, simpler than that!

Lesson learned … again.

See you next time!



  1. A panda walks into a cafe. (Look it up. Or not, I’m not the boss of you.)